dict.c revision 77fbb8ff4ba461c11af3678a0db7cf6a47738ff4
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <assert.h>
5
6#include "common.h"
7
8/*
9  Dictionary based on code by Morten Eriksen <mortene@sim.no>.
10*/
11
12struct dict_entry {
13	unsigned int hash;
14	void *key;
15	void *value;
16	struct dict_entry *next;
17};
18
19/* #define DICTTABLESIZE 97 */
20#define DICTTABLESIZE 997	/* Semi-randomly selected prime number. */
21/* #define DICTTABLESIZE 9973 */
22/* #define DICTTABLESIZE 99991 */
23/* #define DICTTABLESIZE 999983 */
24
25struct dict {
26	struct dict_entry *buckets[DICTTABLESIZE];
27	unsigned int (*key2hash) (const void *);
28	int (*key_cmp) (const void *, const void *);
29};
30
31Dict *
32dict_init(unsigned int (*key2hash) (const void *),
33	  int (*key_cmp) (const void *, const void *))
34{
35	Dict *d;
36	int i;
37
38	debug(DEBUG_FUNCTION, "dict_init()");
39
40	d = malloc(sizeof(Dict));
41	if (!d) {
42		perror("malloc()");
43		exit(1);
44	}
45	for (i = 0; i < DICTTABLESIZE; i++) {	/* better use memset()? */
46		d->buckets[i] = NULL;
47	}
48	d->key2hash = key2hash;
49	d->key_cmp = key_cmp;
50	return d;
51}
52
53void
54dict_clear(Dict *d) {
55	int i;
56	struct dict_entry *entry, *nextentry;
57
58	debug(DEBUG_FUNCTION, "dict_clear()");
59	assert(d);
60	for (i = 0; i < DICTTABLESIZE; i++) {
61		for (entry = d->buckets[i]; entry != NULL; entry = nextentry) {
62			nextentry = entry->next;
63			free(entry);
64		}
65		d->buckets[i] = NULL;
66	}
67	free(d);
68}
69
70int
71dict_enter(Dict *d, void *key, void *value) {
72	struct dict_entry *entry, *newentry;
73	unsigned int hash;
74	unsigned int bucketpos;
75
76	debug(DEBUG_FUNCTION, "dict_enter()");
77
78	hash = d->key2hash(key);
79	bucketpos = hash % DICTTABLESIZE;
80
81	assert(d);
82	newentry = malloc(sizeof(struct dict_entry));
83	if (!newentry) {
84		perror("malloc");
85		exit(1);
86	}
87
88	newentry->hash = hash;
89	newentry->key = key;
90	newentry->value = value;
91	newentry->next = NULL;
92
93	entry = d->buckets[bucketpos];
94	while (entry && entry->next)
95		entry = entry->next;
96
97	if (entry)
98		entry->next = newentry;
99	else
100		d->buckets[bucketpos] = newentry;
101
102	debug(3, "new dict entry at %p[%d]: (%p,%p)", d, bucketpos, key, value);
103	return 0;
104}
105
106void *
107dict_remove(Dict *d, void *key)
108{
109	assert(d != NULL);
110	debug(DEBUG_FUNCTION, "dict_remove(%p)", key);
111
112	unsigned int hash = d->key2hash(key);
113	unsigned int bucketpos = hash % DICTTABLESIZE;
114
115	struct dict_entry **entryp;
116	for (entryp = &d->buckets[bucketpos]; (*entryp) != NULL;
117	     entryp = &(*entryp)->next) {
118		struct dict_entry *entry = *entryp;
119		if (hash != entry->hash)
120			continue;
121		if (d->key_cmp(key, entry->key) == 0) {
122			*entryp = entry->next;
123			return entry->value;
124		}
125	}
126	return NULL;
127}
128
129void *
130dict_find_entry(Dict *d, const void *key)
131{
132	unsigned int hash;
133	unsigned int bucketpos;
134	struct dict_entry *entry;
135
136	debug(DEBUG_FUNCTION, "dict_find_entry()");
137
138	hash = d->key2hash(key);
139	bucketpos = hash % DICTTABLESIZE;
140
141	assert(d);
142	for (entry = d->buckets[bucketpos]; entry; entry = entry->next) {
143		if (hash != entry->hash) {
144			continue;
145		}
146		if (!d->key_cmp(key, entry->key)) {
147			break;
148		}
149	}
150	return entry ? entry->value : NULL;
151}
152
153void
154dict_apply_to_all(Dict *d,
155		  void (*func) (void *key, void *value, void *data), void *data) {
156	int i;
157
158	debug(DEBUG_FUNCTION, "dict_apply_to_all()");
159
160	if (!d) {
161		return;
162	}
163	for (i = 0; i < DICTTABLESIZE; i++) {
164		struct dict_entry *entry = d->buckets[i];
165		while (entry) {
166			func(entry->key, entry->value, data);
167			entry = entry->next;
168		}
169	}
170}
171
172/*****************************************************************************/
173
174unsigned int
175dict_key2hash_string(const void *key)
176{
177	const char *s = (const char *)key;
178	unsigned int total = 0, shift = 0;
179
180	assert(key);
181	while (*s) {
182		total = total ^ ((*s) << shift);
183		shift += 5;
184		if (shift > 24)
185			shift -= 24;
186		s++;
187	}
188	return total;
189}
190
191int
192dict_key_cmp_string(const void *key1, const void *key2)
193{
194	assert(key1);
195	assert(key2);
196	return strcmp((const char *)key1, (const char *)key2);
197}
198
199unsigned int
200dict_key2hash_int(const void *key)
201{
202	return (unsigned long)key;
203}
204
205int
206dict_key_cmp_int(const void *key1, const void *key2)
207{
208	return key1 - key2;
209}
210
211Dict *
212dict_clone2(Dict * old, void * (*key_clone)(void *, void *),
213	    void * (*value_clone)(void *, void *), void * data)
214{
215	Dict *d;
216	int i;
217
218	debug(DEBUG_FUNCTION, "dict_clone()");
219
220	d = malloc(sizeof(Dict));
221	if (!d) {
222		perror("malloc()");
223		exit(1);
224	}
225	memcpy(d, old, sizeof(Dict));
226	for (i = 0; i < DICTTABLESIZE; i++) {	/* better use memset()? */
227		struct dict_entry *de_old;
228		struct dict_entry **de_new;
229
230		de_old = old->buckets[i];
231		de_new = &d->buckets[i];
232		while (de_old) {
233			void * nkey, * nval;
234			*de_new = malloc(sizeof(struct dict_entry));
235			if (!*de_new) {
236				perror("malloc()");
237				exit(1);
238			}
239			memcpy(*de_new, de_old, sizeof(struct dict_entry));
240
241			/* The error detection is rather weak :-/ */
242			nkey = key_clone(de_old->key, data);
243			if (nkey == NULL && de_old->key != NULL) {
244				perror("key_clone");
245			err:
246				/* XXX Will this actually work?  We
247				 * simply memcpy the old dictionary
248				 * over up there.  */
249				dict_clear(d);
250				free(de_new);
251				return NULL;
252			}
253
254			nval = value_clone(de_old->value, data);
255			if (nval == NULL && de_old->value != NULL) {
256				perror("value_clone");
257				goto err;
258			}
259
260			(*de_new)->key = nkey;
261			(*de_new)->value = nval;
262			de_new = &(*de_new)->next;
263			de_old = de_old->next;
264		}
265	}
266	return d;
267}
268
269struct wrap_clone_cb
270{
271	void * (*key_clone)(void *);
272	void * (*value_clone)(void *);
273};
274
275static void *
276value_clone_1(void * arg, void * data)
277{
278	return ((struct wrap_clone_cb *)data)->value_clone(arg);
279}
280
281static void *
282key_clone_1(void * arg, void * data)
283{
284	return ((struct wrap_clone_cb *)data)->key_clone(arg);
285}
286
287Dict *
288dict_clone(Dict * old, void * (*key_clone)(void *),
289	   void * (*value_clone)(void *))
290{
291	struct wrap_clone_cb cb = { key_clone, value_clone };
292	return dict_clone2(old, &key_clone_1, &value_clone_1, &cb);
293}
294