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