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