xref: /openbsd-src/usr.sbin/config/hash.c (revision 1e13aedbe77069799a2e8c1c8e88631fecd8d1dd)
1*1e13aedbSderaadt /*	$OpenBSD: hash.c,v 1.19 2021/11/28 19:26:03 deraadt Exp $	*/
2125b588dSderaadt /*	$NetBSD: hash.c,v 1.4 1996/11/07 22:59:43 gwr Exp $	*/
3608f9123Sniklas 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1992, 1993
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * This software was developed by the Computer Systems Engineering group
9df930be7Sderaadt  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
10df930be7Sderaadt  * contributed to Berkeley.
11df930be7Sderaadt  *
12df930be7Sderaadt  * All advertising materials mentioning features or use of this software
13df930be7Sderaadt  * must display the following acknowledgement:
14df930be7Sderaadt  *	This product includes software developed by the University of
15df930be7Sderaadt  *	California, Lawrence Berkeley Laboratories.
16df930be7Sderaadt  *
17df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
18df930be7Sderaadt  * modification, are permitted provided that the following conditions
19df930be7Sderaadt  * are met:
20df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
21df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
22df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
23df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
24df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
2529295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
26df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
27df930be7Sderaadt  *    without specific prior written permission.
28df930be7Sderaadt  *
29df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39df930be7Sderaadt  * SUCH DAMAGE.
40df930be7Sderaadt  *
41df930be7Sderaadt  *	from: @(#)hash.c	8.1 (Berkeley) 6/6/93
42df930be7Sderaadt  */
43df930be7Sderaadt 
44*1e13aedbSderaadt #include <sys/types.h>
45c6b62359Sedd 
46df930be7Sderaadt #include <stdlib.h>
47df930be7Sderaadt #include <string.h>
48c6b62359Sedd 
49df930be7Sderaadt #include "config.h"
50df930be7Sderaadt 
51df930be7Sderaadt /*
52df930be7Sderaadt  * Interned strings are kept in a hash table.  By making each string
53df930be7Sderaadt  * unique, the program can compare strings by comparing pointers.
54df930be7Sderaadt  */
55df930be7Sderaadt struct hashent {
56df930be7Sderaadt 	struct	hashent *h_next;	/* hash buckets are chained */
57df930be7Sderaadt 	const char *h_name;		/* the string */
58df930be7Sderaadt 	u_int	h_hash;			/* its hash value */
59df930be7Sderaadt 	void	*h_value;		/* other values (for name=value) */
60df930be7Sderaadt };
61df930be7Sderaadt struct hashtab {
62df930be7Sderaadt 	size_t	ht_size;		/* size (power of 2) */
63df930be7Sderaadt 	u_int	ht_mask;		/* == ht_size - 1 */
64df930be7Sderaadt 	u_int	ht_used;		/* number of entries used */
65df930be7Sderaadt 	u_int	ht_lim;			/* when to expand */
66df930be7Sderaadt 	struct	hashent **ht_tab;	/* base of table */
67df930be7Sderaadt };
68df930be7Sderaadt static struct hashtab strings;
69df930be7Sderaadt 
70df930be7Sderaadt /*
71df930be7Sderaadt  * HASHFRACTION controls ht_lim, which in turn controls the average chain
72df930be7Sderaadt  * length.  We allow a few entries, on average, as comparing them is usually
73df930be7Sderaadt  * cheap (the h_hash values prevent a strcmp).
74df930be7Sderaadt  */
75df930be7Sderaadt #define	HASHFRACTION(sz) ((sz) * 3 / 2)
76df930be7Sderaadt 
77df930be7Sderaadt /* round up to next multiple of y, where y is a power of 2 */
78df930be7Sderaadt #define	ROUND(x, y) (((x) + (y) - 1) & ~((y) - 1))
79df930be7Sderaadt 
80815b5809Sderaadt static void ht_init(struct hashtab *, size_t);
81815b5809Sderaadt static void ht_expand(struct hashtab *);
82df930be7Sderaadt 
83df930be7Sderaadt /*
84df930be7Sderaadt  * Initialize a new hash table.  The size must be a power of 2.
85df930be7Sderaadt  */
86df930be7Sderaadt static void
ht_init(struct hashtab * ht,size_t sz)87815b5809Sderaadt ht_init(struct hashtab *ht, size_t sz)
88df930be7Sderaadt {
890ac0d02eSmpech 	struct hashent **h;
900ac0d02eSmpech 	u_int n;
91df930be7Sderaadt 
92c811dcf3Sespie 	h = ereallocarray(NULL, sz, sizeof *h);
93df930be7Sderaadt 	ht->ht_tab = h;
94df930be7Sderaadt 	ht->ht_size = sz;
95df930be7Sderaadt 	ht->ht_mask = sz - 1;
96df930be7Sderaadt 	for (n = 0; n < sz; n++)
97df930be7Sderaadt 		*h++ = NULL;
98df930be7Sderaadt 	ht->ht_used = 0;
99df930be7Sderaadt 	ht->ht_lim = HASHFRACTION(sz);
100df930be7Sderaadt }
101df930be7Sderaadt 
102df930be7Sderaadt /*
103df930be7Sderaadt  * Expand an existing hash table.
104df930be7Sderaadt  */
105df930be7Sderaadt static void
ht_expand(struct hashtab * ht)106815b5809Sderaadt ht_expand(struct hashtab *ht)
107df930be7Sderaadt {
1080ac0d02eSmpech 	struct hashent *p, **h, **oldh, *q;
1090ac0d02eSmpech 	u_int n, i;
110df930be7Sderaadt 
111df930be7Sderaadt 	n = ht->ht_size * 2;
112c811dcf3Sespie 	h = ecalloc(n, sizeof *h);
113df930be7Sderaadt 	oldh = ht->ht_tab;
114df930be7Sderaadt 	n--;
115df930be7Sderaadt 	for (i = ht->ht_size; i != 0; i--) {
116df930be7Sderaadt 		for (p = *oldh++; p != NULL; p = q) {
117df930be7Sderaadt 			q = p->h_next;
118df930be7Sderaadt 			p->h_next = h[p->h_hash & n];
119df930be7Sderaadt 			h[p->h_hash & n] = p;
120df930be7Sderaadt 		}
121df930be7Sderaadt 	}
122df930be7Sderaadt 	free(ht->ht_tab);
123df930be7Sderaadt 	ht->ht_tab = h;
124df930be7Sderaadt 	ht->ht_mask = n;
125df930be7Sderaadt 	ht->ht_size = ++n;
126df930be7Sderaadt 	ht->ht_lim = HASHFRACTION(n);
127df930be7Sderaadt }
128df930be7Sderaadt 
129df930be7Sderaadt /*
130df930be7Sderaadt  * Make a new hash entry, setting its h_next to NULL.
131df930be7Sderaadt  */
1327411eadaSderaadt static __inline struct hashent *
newhashent(const char * name,u_int h)13311fd9fd9Shenning newhashent(const char *name, u_int h)
134df930be7Sderaadt {
1350ac0d02eSmpech 	struct	hashent *hp;
136df930be7Sderaadt 
137*1e13aedbSderaadt 	hp = emalloc(sizeof(*hp));
138df930be7Sderaadt 	hp->h_name = name;
139df930be7Sderaadt 	hp->h_hash = h;
140df930be7Sderaadt 	hp->h_next = NULL;
141df930be7Sderaadt 	return (hp);
142df930be7Sderaadt }
143df930be7Sderaadt 
144df930be7Sderaadt /*
145df930be7Sderaadt  * Hash a string.
146df930be7Sderaadt  */
1477411eadaSderaadt static __inline u_int
hash(const char * str)14811fd9fd9Shenning hash(const char *str)
149df930be7Sderaadt {
1500ac0d02eSmpech 	u_int h;
151df930be7Sderaadt 
152df930be7Sderaadt 	for (h = 0; *str;)
153df930be7Sderaadt 		h = (h << 5) + h + *str++;
154df930be7Sderaadt 	return (h);
155df930be7Sderaadt }
156df930be7Sderaadt 
157df930be7Sderaadt void
initintern(void)158815b5809Sderaadt initintern(void)
159df930be7Sderaadt {
160df930be7Sderaadt 
161df930be7Sderaadt 	ht_init(&strings, 128);
162df930be7Sderaadt }
163df930be7Sderaadt 
164df930be7Sderaadt /*
165df930be7Sderaadt  * Generate a single unique copy of the given string.  We expect this
166df930be7Sderaadt  * function to be used frequently, so it should be fast.
167df930be7Sderaadt  */
168df930be7Sderaadt const char *
intern(const char * s)169815b5809Sderaadt intern(const char *s)
170df930be7Sderaadt {
1710ac0d02eSmpech 	struct hashtab *ht;
1720ac0d02eSmpech 	struct hashent *hp, **hpp;
1730ac0d02eSmpech 	u_int h;
1740ac0d02eSmpech 	char *p;
1750ac0d02eSmpech 	size_t l;
176df930be7Sderaadt 
177df930be7Sderaadt 	ht = &strings;
178df930be7Sderaadt 	h = hash(s);
179df930be7Sderaadt 	hpp = &ht->ht_tab[h & ht->ht_mask];
180df930be7Sderaadt 	for (; (hp = *hpp) != NULL; hpp = &hp->h_next)
181df930be7Sderaadt 		if (hp->h_hash == h && strcmp(hp->h_name, s) == 0)
182df930be7Sderaadt 			return (hp->h_name);
183df930be7Sderaadt 	l = strlen(s) + 1;
184*1e13aedbSderaadt 	p = malloc(l);
185df930be7Sderaadt 	bcopy(s, p, l);
186df930be7Sderaadt 	*hpp = newhashent(p, h);
187df930be7Sderaadt 	if (++ht->ht_used > ht->ht_lim)
188df930be7Sderaadt 		ht_expand(ht);
189df930be7Sderaadt 	return (p);
190df930be7Sderaadt }
191df930be7Sderaadt 
192df930be7Sderaadt struct hashtab *
ht_new(void)193815b5809Sderaadt ht_new(void)
194df930be7Sderaadt {
1950ac0d02eSmpech 	struct hashtab *ht;
196df930be7Sderaadt 
197df930be7Sderaadt 	ht = emalloc(sizeof *ht);
198df930be7Sderaadt 	ht_init(ht, 8);
199df930be7Sderaadt 	return (ht);
200df930be7Sderaadt }
201df930be7Sderaadt 
202df930be7Sderaadt /*
203b23a0a28Sangelos  * Remove.
204b23a0a28Sangelos  */
205b23a0a28Sangelos int
ht_remove(struct hashtab * ht,const char * nam)206815b5809Sderaadt ht_remove(struct hashtab *ht, const char *nam)
207b23a0a28Sangelos {
2080ac0d02eSmpech 	struct hashent *hp, *thp;
2090ac0d02eSmpech 	u_int h;
210b23a0a28Sangelos 
211b23a0a28Sangelos 	h = hash(nam);
212b23a0a28Sangelos 	hp = ht->ht_tab[h & ht->ht_mask];
213b23a0a28Sangelos 	while (hp && hp->h_name == nam)	{
214b23a0a28Sangelos 		ht->ht_tab[h & ht->ht_mask] = hp->h_next;
21596033f1bSangelos 		/* XXX free hp ? */
216b23a0a28Sangelos 		hp = ht->ht_tab[h & ht->ht_mask];
217b23a0a28Sangelos 	}
218b23a0a28Sangelos 
219b23a0a28Sangelos 	if ((hp = ht->ht_tab[h & ht->ht_mask]) == NULL)
220b23a0a28Sangelos 		return (0);
221b23a0a28Sangelos 
222b23a0a28Sangelos 	for (thp = hp->h_next; thp != NULL; thp = hp->h_next) {
223b23a0a28Sangelos 		if (thp->h_name == nam) {
224b23a0a28Sangelos 			hp->h_next = thp->h_next;
22596033f1bSangelos 			/* XXX free thp ? */
226b23a0a28Sangelos 		} else
227b23a0a28Sangelos 			hp = thp;
228b23a0a28Sangelos 	}
229b23a0a28Sangelos 
230b23a0a28Sangelos 	return (0);
231b23a0a28Sangelos }
232b23a0a28Sangelos 
233b23a0a28Sangelos /*
234df930be7Sderaadt  * Insert and/or replace.
235df930be7Sderaadt  */
236df930be7Sderaadt int
ht_insrep(struct hashtab * ht,const char * nam,void * val,int replace)237815b5809Sderaadt ht_insrep(struct hashtab *ht, const char *nam, void *val, int replace)
238df930be7Sderaadt {
2390ac0d02eSmpech 	struct hashent *hp, **hpp;
2400ac0d02eSmpech 	u_int h;
241df930be7Sderaadt 
242df930be7Sderaadt 	h = hash(nam);
243df930be7Sderaadt 	hpp = &ht->ht_tab[h & ht->ht_mask];
244df930be7Sderaadt 	for (; (hp = *hpp) != NULL; hpp = &hp->h_next) {
245df930be7Sderaadt 		if (hp->h_name == nam) {
246df930be7Sderaadt 			if (replace)
247df930be7Sderaadt 				hp->h_value = val;
248df930be7Sderaadt 			return (1);
249df930be7Sderaadt 		}
250df930be7Sderaadt 	}
251df930be7Sderaadt 	*hpp = hp = newhashent(nam, h);
252df930be7Sderaadt 	hp->h_value = val;
253f3bae140Sderaadt 	if (++ht->ht_used > ht->ht_lim)
254f3bae140Sderaadt 		ht_expand(ht);
255df930be7Sderaadt 	return (0);
256df930be7Sderaadt }
257df930be7Sderaadt 
258df930be7Sderaadt void *
ht_lookup(struct hashtab * ht,const char * nam)259815b5809Sderaadt ht_lookup(struct hashtab *ht, const char *nam)
260df930be7Sderaadt {
2610ac0d02eSmpech 	struct hashent *hp, **hpp;
2620ac0d02eSmpech 	u_int h;
263df930be7Sderaadt 
264df930be7Sderaadt 	h = hash(nam);
265df930be7Sderaadt 	hpp = &ht->ht_tab[h & ht->ht_mask];
266df930be7Sderaadt 	for (; (hp = *hpp) != NULL; hpp = &hp->h_next)
267df930be7Sderaadt 		if (hp->h_name == nam)
268df930be7Sderaadt 			return (hp->h_value);
269df930be7Sderaadt 	return (NULL);
270df930be7Sderaadt }
271