xref: /minix3/lib/libc/stdlib/hcreate.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /* $NetBSD: hcreate.c,v 1.10 2014/07/20 20:17:21 christos Exp $ */
22fe8fb19SBen Gras 
32fe8fb19SBen Gras /*
42fe8fb19SBen Gras  * Copyright (c) 2001 Christopher G. Demetriou
52fe8fb19SBen Gras  * All rights reserved.
62fe8fb19SBen Gras  *
72fe8fb19SBen Gras  * Redistribution and use in source and binary forms, with or without
82fe8fb19SBen Gras  * modification, are permitted provided that the following conditions
92fe8fb19SBen Gras  * are met:
102fe8fb19SBen Gras  * 1. Redistributions of source code must retain the above copyright
112fe8fb19SBen Gras  *    notice, this list of conditions and the following disclaimer.
122fe8fb19SBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
132fe8fb19SBen Gras  *    notice, this list of conditions and the following disclaimer in the
142fe8fb19SBen Gras  *    documentation and/or other materials provided with the distribution.
15f14fb602SLionel Sambuc  * 3. The name of the author may not be used to endorse or promote products
162fe8fb19SBen Gras  *    derived from this software without specific prior written permission.
172fe8fb19SBen Gras  *
182fe8fb19SBen Gras  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
192fe8fb19SBen Gras  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
202fe8fb19SBen Gras  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
212fe8fb19SBen Gras  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
222fe8fb19SBen Gras  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
232fe8fb19SBen Gras  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
242fe8fb19SBen Gras  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
252fe8fb19SBen Gras  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
262fe8fb19SBen Gras  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
272fe8fb19SBen Gras  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
282fe8fb19SBen Gras  *
292fe8fb19SBen Gras  * <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
302fe8fb19SBen Gras  */
312fe8fb19SBen Gras 
322fe8fb19SBen Gras /*
332fe8fb19SBen Gras  * hcreate() / hsearch() / hdestroy()
342fe8fb19SBen Gras  *
352fe8fb19SBen Gras  * SysV/XPG4 hash table functions.
362fe8fb19SBen Gras  *
372fe8fb19SBen Gras  * Implementation done based on NetBSD manual page and Solaris manual page,
382fe8fb19SBen Gras  * plus my own personal experience about how they're supposed to work.
392fe8fb19SBen Gras  *
402fe8fb19SBen Gras  * I tried to look at Knuth (as cited by the Solaris manual page), but
412fe8fb19SBen Gras  * nobody had a copy in the office, so...
422fe8fb19SBen Gras  */
432fe8fb19SBen Gras 
442fe8fb19SBen Gras #include <sys/cdefs.h>
452fe8fb19SBen Gras #if defined(LIBC_SCCS) && !defined(lint)
46*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: hcreate.c,v 1.10 2014/07/20 20:17:21 christos Exp $");
472fe8fb19SBen Gras #endif /* LIBC_SCCS and not lint */
482fe8fb19SBen Gras 
492fe8fb19SBen Gras #if !defined(lint)
502fe8fb19SBen Gras __COPYRIGHT("@(#) Copyright (c) 2001\
512fe8fb19SBen Gras  Christopher G. Demetriou.  All rights reserved.");
522fe8fb19SBen Gras #endif /* not lint */
532fe8fb19SBen Gras 
542fe8fb19SBen Gras #include "namespace.h"
552fe8fb19SBen Gras #include <assert.h>
562fe8fb19SBen Gras #include <errno.h>
572fe8fb19SBen Gras #include <inttypes.h>
582fe8fb19SBen Gras #include <search.h>
592fe8fb19SBen Gras #include <stdlib.h>
602fe8fb19SBen Gras #include <string.h>
612fe8fb19SBen Gras #include <sys/queue.h>
622fe8fb19SBen Gras 
632fe8fb19SBen Gras /*
642fe8fb19SBen Gras  * DO NOT MAKE THIS STRUCTURE LARGER THAN 32 BYTES (4 ptrs on 64-bit
652fe8fb19SBen Gras  * ptr machine) without adjusting MAX_BUCKETS_LG2 below.
662fe8fb19SBen Gras  */
672fe8fb19SBen Gras struct internal_entry {
682fe8fb19SBen Gras 	SLIST_ENTRY(internal_entry) link;
692fe8fb19SBen Gras 	ENTRY ent;
702fe8fb19SBen Gras };
712fe8fb19SBen Gras SLIST_HEAD(internal_head, internal_entry);
722fe8fb19SBen Gras 
732fe8fb19SBen Gras #define	MIN_BUCKETS_LG2	4
742fe8fb19SBen Gras #define	MIN_BUCKETS	(1 << MIN_BUCKETS_LG2)
752fe8fb19SBen Gras 
762fe8fb19SBen Gras /*
772fe8fb19SBen Gras  * max * sizeof internal_entry must fit into size_t.
782fe8fb19SBen Gras  * assumes internal_entry is <= 32 (2^5) bytes.
792fe8fb19SBen Gras  */
802fe8fb19SBen Gras #define	MAX_BUCKETS_LG2	(sizeof (size_t) * 8 - 1 - 5)
812fe8fb19SBen Gras #define	MAX_BUCKETS	((size_t)1 << MAX_BUCKETS_LG2)
822fe8fb19SBen Gras 
832fe8fb19SBen Gras /* Default hash function, from db/hash/hash_func.c */
842fe8fb19SBen Gras extern u_int32_t (*__default_hash)(const void *, size_t);
852fe8fb19SBen Gras 
86f14fb602SLionel Sambuc static struct hsearch_data htable;
872fe8fb19SBen Gras 
882fe8fb19SBen Gras int
hcreate(size_t nel)892fe8fb19SBen Gras hcreate(size_t nel)
902fe8fb19SBen Gras {
91f14fb602SLionel Sambuc 	_DIAGASSERT(htable.table == NULL);
922fe8fb19SBen Gras 
932fe8fb19SBen Gras 	/* Make sure this isn't called when a table already exists. */
94f14fb602SLionel Sambuc 	if (htable.table != NULL) {
952fe8fb19SBen Gras 		errno = EINVAL;
962fe8fb19SBen Gras 		return 0;
972fe8fb19SBen Gras 	}
98f14fb602SLionel Sambuc 	return hcreate_r(nel, &htable);
99f14fb602SLionel Sambuc }
100f14fb602SLionel Sambuc 
101f14fb602SLionel Sambuc int
hcreate_r(size_t nel,struct hsearch_data * head)102f14fb602SLionel Sambuc hcreate_r(size_t nel, struct hsearch_data *head)
103f14fb602SLionel Sambuc {
104f14fb602SLionel Sambuc 	struct internal_head *table;
105f14fb602SLionel Sambuc 	size_t idx;
106f14fb602SLionel Sambuc 	unsigned int p2;
107f14fb602SLionel Sambuc 	void *p;
1082fe8fb19SBen Gras 
1092fe8fb19SBen Gras 	/* If nel is too small, make it min sized. */
1102fe8fb19SBen Gras 	if (nel < MIN_BUCKETS)
1112fe8fb19SBen Gras 		nel = MIN_BUCKETS;
1122fe8fb19SBen Gras 
1132fe8fb19SBen Gras 	/* If it's too large, cap it. */
1142fe8fb19SBen Gras 	if (nel > MAX_BUCKETS)
1152fe8fb19SBen Gras 		nel = MAX_BUCKETS;
1162fe8fb19SBen Gras 
1172fe8fb19SBen Gras 	/* If it's is not a power of two in size, round up. */
1182fe8fb19SBen Gras 	if ((nel & (nel - 1)) != 0) {
1192fe8fb19SBen Gras 		for (p2 = 0; nel != 0; p2++)
1202fe8fb19SBen Gras 			nel >>= 1;
1212fe8fb19SBen Gras 		_DIAGASSERT(p2 <= MAX_BUCKETS_LG2);
1222fe8fb19SBen Gras 		nel = 1 << p2;
1232fe8fb19SBen Gras 	}
1242fe8fb19SBen Gras 
1252fe8fb19SBen Gras 	/* Allocate the table. */
126f14fb602SLionel Sambuc 	head->size = nel;
127f14fb602SLionel Sambuc 	head->filled = 0;
128f14fb602SLionel Sambuc 	p = malloc(nel * sizeof table[0]);
129f14fb602SLionel Sambuc 	if (p == NULL) {
1302fe8fb19SBen Gras 		errno = ENOMEM;
1312fe8fb19SBen Gras 		return 0;
1322fe8fb19SBen Gras 	}
133f14fb602SLionel Sambuc 	head->table = p;
134f14fb602SLionel Sambuc 	table = p;
1352fe8fb19SBen Gras 
1362fe8fb19SBen Gras 	/* Initialize it. */
137f14fb602SLionel Sambuc 	for (idx = 0; idx < nel; idx++)
138f14fb602SLionel Sambuc 		SLIST_INIT(&table[idx]);
1392fe8fb19SBen Gras 
1402fe8fb19SBen Gras 	return 1;
1412fe8fb19SBen Gras }
1422fe8fb19SBen Gras 
1432fe8fb19SBen Gras void
hdestroy1(void (* freekey)(void *),void (* freedata)(void *))144*0a6a1f1dSLionel Sambuc hdestroy1(void (*freekey)(void *), void (*freedata)(void *))
1452fe8fb19SBen Gras {
146f14fb602SLionel Sambuc 	_DIAGASSERT(htable.table != NULL);
147*0a6a1f1dSLionel Sambuc 	hdestroy1_r(&htable, freekey, freedata);
148f14fb602SLionel Sambuc }
149f14fb602SLionel Sambuc 
150f14fb602SLionel Sambuc void
hdestroy(void)151*0a6a1f1dSLionel Sambuc hdestroy(void)
152*0a6a1f1dSLionel Sambuc {
153*0a6a1f1dSLionel Sambuc 	hdestroy1(NULL, NULL);
154*0a6a1f1dSLionel Sambuc }
155*0a6a1f1dSLionel Sambuc 
156*0a6a1f1dSLionel Sambuc void
hdestroy1_r(struct hsearch_data * head,void (* freekey)(void *),void (* freedata)(void *))157*0a6a1f1dSLionel Sambuc hdestroy1_r(struct hsearch_data *head, void (*freekey)(void *),
158*0a6a1f1dSLionel Sambuc     void (*freedata)(void *))
159f14fb602SLionel Sambuc {
1602fe8fb19SBen Gras 	struct internal_entry *ie;
1612fe8fb19SBen Gras 	size_t idx;
162f14fb602SLionel Sambuc 	void *p;
163f14fb602SLionel Sambuc 	struct internal_head *table;
1642fe8fb19SBen Gras 
165f14fb602SLionel Sambuc 	if (head == NULL)
1662fe8fb19SBen Gras 		return;
1672fe8fb19SBen Gras 
168f14fb602SLionel Sambuc 	p = head->table;
169f14fb602SLionel Sambuc 	head->table = NULL;
170f14fb602SLionel Sambuc 	table = p;
171f14fb602SLionel Sambuc 
172f14fb602SLionel Sambuc 	for (idx = 0; idx < head->size; idx++) {
173f14fb602SLionel Sambuc 		while (!SLIST_EMPTY(&table[idx])) {
174f14fb602SLionel Sambuc 			ie = SLIST_FIRST(&table[idx]);
175f14fb602SLionel Sambuc 			SLIST_REMOVE_HEAD(&table[idx], link);
176*0a6a1f1dSLionel Sambuc 			if (freekey)
177*0a6a1f1dSLionel Sambuc 				(*freekey)(ie->ent.key);
178*0a6a1f1dSLionel Sambuc 			if (freedata)
179*0a6a1f1dSLionel Sambuc 				(*freedata)(ie->ent.data);
1802fe8fb19SBen Gras 			free(ie);
1812fe8fb19SBen Gras 		}
1822fe8fb19SBen Gras 	}
183f14fb602SLionel Sambuc 	free(table);
1842fe8fb19SBen Gras }
1852fe8fb19SBen Gras 
186*0a6a1f1dSLionel Sambuc void
hdestroy_r(struct hsearch_data * head)187*0a6a1f1dSLionel Sambuc hdestroy_r(struct hsearch_data *head)
188*0a6a1f1dSLionel Sambuc {
189*0a6a1f1dSLionel Sambuc 	hdestroy1_r(head, NULL, NULL);
190*0a6a1f1dSLionel Sambuc }
191*0a6a1f1dSLionel Sambuc 
1922fe8fb19SBen Gras ENTRY *
hsearch(ENTRY item,ACTION action)1932fe8fb19SBen Gras hsearch(ENTRY item, ACTION action)
1942fe8fb19SBen Gras {
195f14fb602SLionel Sambuc 	ENTRY *ep;
196f14fb602SLionel Sambuc 	_DIAGASSERT(htable.table != NULL);
197f14fb602SLionel Sambuc 	(void)hsearch_r(item, action, &ep, &htable);
198f14fb602SLionel Sambuc 	return ep;
199f14fb602SLionel Sambuc }
200f14fb602SLionel Sambuc 
201f14fb602SLionel Sambuc int
hsearch_r(ENTRY item,ACTION action,ENTRY ** itemp,struct hsearch_data * head)202f14fb602SLionel Sambuc hsearch_r(ENTRY item, ACTION action, ENTRY **itemp, struct hsearch_data *head)
203f14fb602SLionel Sambuc {
204f14fb602SLionel Sambuc 	struct internal_head *table, *chain;
2052fe8fb19SBen Gras 	struct internal_entry *ie;
2062fe8fb19SBen Gras 	uint32_t hashval;
2072fe8fb19SBen Gras 	size_t len;
208f14fb602SLionel Sambuc 	void *p;
2092fe8fb19SBen Gras 
2102fe8fb19SBen Gras 	_DIAGASSERT(item.key != NULL);
2112fe8fb19SBen Gras 	_DIAGASSERT(action == ENTER || action == FIND);
2122fe8fb19SBen Gras 
213f14fb602SLionel Sambuc 	p = head->table;
214f14fb602SLionel Sambuc 	table = p;
215f14fb602SLionel Sambuc 
2162fe8fb19SBen Gras 	len = strlen(item.key);
2172fe8fb19SBen Gras 	hashval = (*__default_hash)(item.key, len);
2182fe8fb19SBen Gras 
219f14fb602SLionel Sambuc 	chain = &table[hashval & (head->size - 1)];
220f14fb602SLionel Sambuc 	ie = SLIST_FIRST(chain);
2212fe8fb19SBen Gras 	while (ie != NULL) {
2222fe8fb19SBen Gras 		if (strcmp(ie->ent.key, item.key) == 0)
2232fe8fb19SBen Gras 			break;
2242fe8fb19SBen Gras 		ie = SLIST_NEXT(ie, link);
2252fe8fb19SBen Gras 	}
2262fe8fb19SBen Gras 
227f14fb602SLionel Sambuc 	if (ie != NULL) {
228f14fb602SLionel Sambuc 		*itemp = &ie->ent;
229f14fb602SLionel Sambuc 		return 1;
230f14fb602SLionel Sambuc 	} else if (action == FIND) {
231f14fb602SLionel Sambuc 		*itemp = NULL;
232f14fb602SLionel Sambuc 		errno = ESRCH;
233f14fb602SLionel Sambuc 		return 1;
234f14fb602SLionel Sambuc 	}
2352fe8fb19SBen Gras 
2362fe8fb19SBen Gras 	ie = malloc(sizeof *ie);
2372fe8fb19SBen Gras 	if (ie == NULL)
238f14fb602SLionel Sambuc 		return 0;
2392fe8fb19SBen Gras 	ie->ent.key = item.key;
2402fe8fb19SBen Gras 	ie->ent.data = item.data;
2412fe8fb19SBen Gras 
242f14fb602SLionel Sambuc 	SLIST_INSERT_HEAD(chain, ie, link);
243f14fb602SLionel Sambuc 	*itemp = &ie->ent;
244f14fb602SLionel Sambuc 	head->filled++;
245f14fb602SLionel Sambuc 	return 1;
2462fe8fb19SBen Gras }
247