xref: /dragonfly/usr.sbin/installer/libaura/dict.c (revision dadd6466)
1 /*
2  * Copyright (c) 2004 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Chris Pressey <cpressey@catseye.mine.nu>.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 /*
36  * dict.c
37  * $Id: dict.c,v 1.4 2005/02/06 06:57:30 cpressey Exp $
38  * Routines to manipulate dictionaries.
39  */
40 
41 #include <stdlib.h>
42 #include <string.h>
43 
44 #include "mem.h"
45 
46 #include "dict.h"
47 
48 /*** INTERNAL PROTOTYPES ***/
49 
50 size_t	hashpjw(const void *key, size_t key_size, size_t table_size);
51 int	keycmp(const void *key, size_t key_size, struct aura_bucket *b);
52 
53 struct aura_bucket *aura_bucket_new(const void *key, size_t key_size,
54 				const void *data, size_t data_size);
55 void	aura_bucket_free(struct aura_bucket *b);
56 void	aura_dict_locate_hash(struct aura_dict *d, const void *key, size_t key_size,
57 				size_t *b_index, struct aura_bucket **b);
58 void	aura_dict_locate_list(struct aura_dict *d, const void *key, size_t key_size,
59 				struct aura_bucket **b);
60 void	aura_dict_advance(struct aura_dict *d);
61 
62 void	aura_dict_fetch_hash(struct aura_dict *d, const void *key, size_t key_size,
63 				void **data, size_t *data_size);
64 void	aura_dict_store_hash(struct aura_dict *d, const void *key, size_t key_size,
65 				const void *data, size_t data_size);
66 void	aura_dict_fetch_list(struct aura_dict *d, const void *key, size_t key_size,
67 				void **data, size_t *data_size);
68 void	aura_dict_store_list(struct aura_dict *d, const void *key, size_t key_size,
69 				const void *data, size_t data_size);
70 void	aura_dict_store_list_sorted(struct aura_dict *d, const void *key, size_t key_size,
71 				const void *data, size_t data_size);
72 
73 /*** CONSTRUCTOR ***/
74 
75 /*
76  * Create a new dictionary with the given number of buckets.
77  */
78 struct aura_dict *
79 aura_dict_new(size_t num_buckets, int method)
80 {
81 	struct aura_dict *d;
82 	size_t i;
83 
84 	AURA_MALLOC(d, aura_dict);
85 
86 	d->num_buckets = num_buckets;
87 	d->b = malloc(sizeof(struct bucket *) * num_buckets);
88 	for (i = 0; i < num_buckets; i++) {
89 		d->b[i] = NULL;
90 	}
91 	d->cursor = NULL;
92 	d->cur_bucket = 0;
93 
94 	switch (method) {
95 	case AURA_DICT_HASH:
96 		d->fetch = aura_dict_fetch_hash;
97 		d->store = aura_dict_store_hash;
98 		break;
99 	case AURA_DICT_LIST:
100 		d->fetch = aura_dict_fetch_list;
101 		d->store = aura_dict_store_list;
102 		break;
103 	case AURA_DICT_SORTED_LIST:
104 		d->fetch = aura_dict_fetch_list;
105 		d->store = aura_dict_store_list_sorted;
106 		break;
107 	}
108 
109 	return(d);
110 }
111 
112 /*** DESTRUCTORS ***/
113 
114 void
115 aura_bucket_free(struct aura_bucket *b)
116 {
117 	if (b == NULL)
118 		return;
119 	if (b->key != NULL)
120 		free(b->key);
121 	if (b->data != NULL)
122 		free(b->data);
123 	AURA_FREE(b, aura_bucket);
124 }
125 
126 void
127 aura_dict_free(struct aura_dict *d)
128 {
129 	struct aura_bucket *b;
130 	size_t bucket_no = 0;
131 
132 	while (bucket_no < d->num_buckets) {
133 		b = d->b[bucket_no];
134 		while (b != NULL) {
135 			d->b[bucket_no] = b->next;
136 			aura_bucket_free(b);
137 			b = d->b[bucket_no];
138 		}
139 		bucket_no++;
140 	}
141 	AURA_FREE(d, aura_dict);
142 }
143 
144 /*** UTILITIES ***/
145 
146 /*
147  * Hash function, taken from "Compilers: Principles, Techniques, and Tools"
148  * by Aho, Sethi, & Ullman (a.k.a. "The Dragon Book", 2nd edition.)
149  */
150 size_t
151 hashpjw(const void *key, size_t key_size, size_t table_size) {
152 	const char *k = (const char *)key;
153 	const char *p;
154 	size_t h = 0, g;
155 
156 	for (p = k; p < k + key_size; p++) {
157 		h = (h << 4) + (*p);
158 		if ((g = h & 0xf0000000))
159 			h = (h ^ (g >> 24)) ^ g;
160 	}
161 
162 	return(h % table_size);
163 }
164 
165 /*
166  * Create a new bucket (not called directly by client code.)
167  * Uses a copy of key and data for the bucket, so the dictionary
168  * code is responsible for cleaning it up itself.
169  */
170 struct aura_bucket *
171 aura_bucket_new(const void *key, size_t key_size, const void *data, size_t data_size)
172 {
173 	struct aura_bucket *b;
174 
175 	AURA_MALLOC(b, aura_bucket);
176 
177 	b->next = NULL;
178 	b->key = aura_malloc(key_size, "dictionary key");
179 	memcpy(b->key, key, key_size);
180 	b->key_size = key_size;
181 	b->data = aura_malloc(data_size, "dictionary value");
182 	memcpy(b->data, data, data_size);
183 	b->data_size = data_size;
184 
185 	return(b);
186 }
187 
188 /*
189  * Locate the bucket number a particular key would be located in, and the
190  * bucket itself if such a key exists (or NULL if it could not be found.)
191  */
192 void
193 aura_dict_locate_hash(struct aura_dict *d, const void *key, size_t key_size,
194 		      size_t *b_index, struct aura_bucket **b)
195 {
196 	*b_index = hashpjw(key, key_size, d->num_buckets);
197 	for (*b = d->b[*b_index]; *b != NULL; *b = (*b)->next) {
198 		if (key_size == (*b)->key_size && memcmp(key, (*b)->key, key_size) == 0)
199 			break;
200 	}
201 }
202 
203 /*
204  * Locate the bucket a particular key would be located in
205  * if such a key exists (or NULL if it could not be found.)
206  */
207 void
208 aura_dict_locate_list(struct aura_dict *d, const void *key, size_t key_size,
209 		      struct aura_bucket **b)
210 {
211 	for (*b = d->b[0]; *b != NULL; *b = (*b)->next) {
212 		if (key_size == (*b)->key_size && memcmp(key, (*b)->key, key_size) == 0)
213 			break;
214 	}
215 }
216 
217 /*** IMPLEMENTATIONS ***/
218 
219 /***** HASH TABLE *****/
220 
221 void
222 aura_dict_fetch_hash(struct aura_dict *d, const void *key, size_t key_size,
223 		     void **data, size_t *data_size)
224 {
225 	struct aura_bucket *b;
226 	size_t i;
227 
228 	aura_dict_locate_hash(d, key, key_size, &i, &b);
229 	if (b != NULL) {
230 		*data = b->data;
231 		*data_size = b->data_size;
232 	} else {
233 		*data = NULL;
234 	}
235 }
236 
237 void
238 aura_dict_store_hash(struct aura_dict *d, const void *key, size_t key_size,
239 		     const void *data, size_t data_size)
240 {
241 	struct aura_bucket *b;
242 	size_t i;
243 
244 	aura_dict_locate_hash(d, key, key_size, &i, &b);
245 	if (b == NULL) {
246 		/* Bucket does not exist, add a new one. */
247 		b = aura_bucket_new(key, key_size, data, data_size);
248 		b->next = d->b[i];
249 		d->b[i] = b;
250 	} else {
251 		/* Bucket already exists, replace the value. */
252 		aura_free(b->data, "dictionary value");
253 		b->data = aura_malloc(data_size, "dictionary value");
254 		memcpy(b->data, data, data_size);
255 		b->data_size = data_size;
256 	}
257 }
258 
259 /***** LIST *****/
260 
261 void
262 aura_dict_fetch_list(struct aura_dict *d, const void *key, size_t key_size,
263 		     void **data, size_t *data_size)
264 {
265 	struct aura_bucket *b;
266 
267 	for (b = d->b[0]; b != NULL; b = b->next) {
268 		if (key_size == b->key_size && memcmp(key, b->key, key_size) == 0) {
269 			*data = b->data;
270 			*data_size = b->data_size;
271 			return;
272 		}
273 	}
274 	*data = NULL;
275 }
276 
277 void
278 aura_dict_store_list(struct aura_dict *d, const void *key, size_t key_size,
279 		     const void *data, size_t data_size)
280 {
281 	struct aura_bucket *b;
282 
283 	aura_dict_locate_list(d, key, key_size, &b);
284 	if (b == NULL) {
285 		/* Bucket does not exist, add a new one. */
286 		b = aura_bucket_new(key, key_size, data, data_size);
287 		b->next = d->b[0];
288 		d->b[0] = b;
289 	} else {
290 		/* Bucket already exists, replace the value. */
291 		aura_free(b->data, "dictionary value");
292 		b->data = aura_malloc(data_size, "dictionary value");
293 		memcpy(b->data, data, data_size);
294 		b->data_size = data_size;
295 	}
296 }
297 
298 /***** SORTED LIST *****/
299 
300 int
301 keycmp(const void *key, size_t key_size, struct aura_bucket *b)
302 {
303 	int r;
304 
305 	if ((r = memcmp(key, b->key,
306 	    b->key_size < key_size ? b->key_size : key_size)) == 0) {
307 		if (key_size < b->key_size)
308 			return(-1);
309 		if (key_size > b->key_size)
310 			return(1);
311 		return(0);
312 	}
313 	return(r);
314 }
315 
316 void
317 aura_dict_store_list_sorted(struct aura_dict *d, const void *key, size_t key_size,
318 			    const void *data, size_t data_size)
319 {
320 	struct aura_bucket *b, *new_b, *prev = NULL;
321 	int added = 0;
322 
323 	/* XXX could be more efficient. */
324 	aura_dict_locate_list(d, key, key_size, &b);
325 	if (b == NULL) {
326 		new_b = aura_bucket_new(key, key_size, data, data_size);
327 		if (d->b[0] == NULL) {
328 			/*
329 			 * Special case: insert at head
330 			 * if bucket is empty.
331 			 */
332 			new_b->next = NULL;
333 			d->b[0] = new_b;
334 		} else {
335 			for (b = d->b[0]; b != NULL; b = b->next) {
336 				/* XXX if identical - no need for above fetch */
337 				if (keycmp(key, key_size, b) < 0) {
338 					if (prev != NULL)
339 						prev->next = new_b;
340 					else
341 						d->b[0] = new_b;
342 					new_b->next = b;
343 					added = 1;
344 					break;
345 				}
346 				prev = b;
347 			}
348 			if (!added) {
349 				prev->next = new_b;
350 				new_b->next = NULL;
351 			}
352 		}
353 	} else {
354 		/* Bucket already exists, replace the value. */
355 		aura_free(b->data, "dictionary value");
356 		b->data = aura_malloc(data_size, "dictionary value");
357 		memcpy(b->data, data, data_size);
358 		b->data_size = data_size;
359 	}
360 }
361 
362 /*** INTERFACE ***/
363 
364 /*
365  * Retrieve a value from a dictionary, give its key.  The value and its
366  * size are returned in *data and *data_size.  If no value could be
367  * found, *data is set to NULL.
368  */
369 void
370 aura_dict_fetch(struct aura_dict *d, const void *key, size_t key_size,
371 		void **data, size_t *data_size)
372 {
373 	d->fetch(d, key, key_size, data, data_size);
374 }
375 
376 int
377 aura_dict_exists(struct aura_dict *d, const void *key, size_t key_size)
378 {
379 	void *data;
380 	size_t data_size;
381 
382 	d->fetch(d, key, key_size, &data, &data_size);
383 	return(data != NULL);
384 }
385 
386 /*
387  * Insert a value into a dictionary, if it does not exist.
388  */
389 void
390 aura_dict_store(struct aura_dict *d, const void *key, size_t key_size,
391 		const void *data, size_t data_size)
392 {
393 	d->store(d, key, key_size, data, data_size);
394 }
395 
396 /*
397  * Finds the next bucket with data in it.
398  * If d->cursor == NULL after this, there is no more data.
399  */
400 void
401 aura_dict_advance(struct aura_dict *d)
402 {
403 	while (d->cursor == NULL) {
404 		if (d->cur_bucket == d->num_buckets - 1) {
405 			/* We're at eof.  Do nothing. */
406 			break;
407 		} else {
408 			d->cur_bucket++;
409 			d->cursor = d->b[d->cur_bucket];
410 		}
411 	}
412 }
413 
414 void
415 aura_dict_rewind(struct aura_dict *d)
416 {
417 	d->cur_bucket = 0;
418 	d->cursor = d->b[d->cur_bucket];
419 	aura_dict_advance(d);
420 }
421 
422 int
423 aura_dict_eof(struct aura_dict *d)
424 {
425 	return(d->cursor == NULL);
426 }
427 
428 void
429 aura_dict_get_current_key(struct aura_dict *d, void **key, size_t *key_size)
430 {
431 	if (d->cursor == NULL) {
432 		*key = NULL;
433 	} else {
434 		*key = d->cursor->key;
435 		*key_size = d->cursor->key_size;
436 	}
437 }
438 
439 void
440 aura_dict_next(struct aura_dict *d)
441 {
442 	if (d->cursor != NULL)
443 		d->cursor = d->cursor->next;
444 	aura_dict_advance(d);
445 }
446 
447 size_t
448 aura_dict_size(struct aura_dict *d)
449 {
450 	struct aura_bucket *b;
451 	size_t bucket_no = 0;
452 	size_t count = 0;
453 
454 	while (bucket_no < d->num_buckets) {
455 		b = d->b[bucket_no];
456 		while (b != NULL) {
457 			b = b->next;
458 			count++;
459 		}
460 		bucket_no++;
461 	}
462 
463 	return(count);
464 }
465