xref: /netbsd-src/external/mpl/bind/dist/lib/isc/symtab.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: symtab.c,v 1.7 2025/01/26 16:25:38 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 
18 #include <stdbool.h>
19 
20 #include <isc/ascii.h>
21 #include <isc/magic.h>
22 #include <isc/mem.h>
23 #include <isc/string.h>
24 #include <isc/symtab.h>
25 #include <isc/util.h>
26 
27 typedef struct elt {
28 	char *key;
29 	unsigned int type;
30 	isc_symvalue_t value;
31 	LINK(struct elt) link;
32 } elt_t;
33 
34 typedef LIST(elt_t) eltlist_t;
35 
36 #define SYMTAB_MAGIC	 ISC_MAGIC('S', 'y', 'm', 'T')
37 #define VALID_SYMTAB(st) ISC_MAGIC_VALID(st, SYMTAB_MAGIC)
38 
39 struct isc_symtab {
40 	/* Unlocked. */
41 	unsigned int magic;
42 	isc_mem_t *mctx;
43 	unsigned int size;
44 	unsigned int count;
45 	unsigned int maxload;
46 	eltlist_t *table;
47 	isc_symtabaction_t undefine_action;
48 	void *undefine_arg;
49 	bool case_sensitive;
50 };
51 
52 isc_result_t
53 isc_symtab_create(isc_mem_t *mctx, unsigned int size,
54 		  isc_symtabaction_t undefine_action, void *undefine_arg,
55 		  bool case_sensitive, isc_symtab_t **symtabp) {
56 	isc_symtab_t *symtab;
57 	unsigned int i;
58 
59 	REQUIRE(mctx != NULL);
60 	REQUIRE(symtabp != NULL && *symtabp == NULL);
61 	REQUIRE(size > 0); /* Should be prime. */
62 
63 	symtab = isc_mem_get(mctx, sizeof(*symtab));
64 
65 	symtab->mctx = NULL;
66 	isc_mem_attach(mctx, &symtab->mctx);
67 	symtab->table = isc_mem_cget(mctx, size, sizeof(eltlist_t));
68 	for (i = 0; i < size; i++) {
69 		INIT_LIST(symtab->table[i]);
70 	}
71 	symtab->size = size;
72 	symtab->count = 0;
73 	symtab->maxload = size * 3 / 4;
74 	symtab->undefine_action = undefine_action;
75 	symtab->undefine_arg = undefine_arg;
76 	symtab->case_sensitive = case_sensitive;
77 	symtab->magic = SYMTAB_MAGIC;
78 
79 	*symtabp = symtab;
80 
81 	return ISC_R_SUCCESS;
82 }
83 
84 void
85 isc_symtab_destroy(isc_symtab_t **symtabp) {
86 	isc_symtab_t *symtab;
87 	unsigned int i;
88 	elt_t *elt, *nelt;
89 
90 	REQUIRE(symtabp != NULL);
91 	symtab = *symtabp;
92 	*symtabp = NULL;
93 	REQUIRE(VALID_SYMTAB(symtab));
94 
95 	for (i = 0; i < symtab->size; i++) {
96 		for (elt = HEAD(symtab->table[i]); elt != NULL; elt = nelt) {
97 			nelt = NEXT(elt, link);
98 			if (symtab->undefine_action != NULL) {
99 				(symtab->undefine_action)(elt->key, elt->type,
100 							  elt->value,
101 							  symtab->undefine_arg);
102 			}
103 			isc_mem_put(symtab->mctx, elt, sizeof(*elt));
104 		}
105 	}
106 	isc_mem_cput(symtab->mctx, symtab->table, symtab->size,
107 		     sizeof(eltlist_t));
108 	symtab->magic = 0;
109 	isc_mem_putanddetach(&symtab->mctx, symtab, sizeof(*symtab));
110 }
111 
112 static unsigned int
113 hash(const char *key, bool case_sensitive) {
114 	const char *s;
115 	unsigned int h = 0;
116 
117 	/*
118 	 * This hash function is similar to the one Ousterhout
119 	 * uses in Tcl.
120 	 */
121 
122 	if (case_sensitive) {
123 		for (s = key; *s != '\0'; s++) {
124 			h += (h << 3) + *s;
125 		}
126 	} else {
127 		for (s = key; *s != '\0'; s++) {
128 			h += (h << 3) + isc_ascii_tolower(*s);
129 		}
130 	}
131 
132 	return h;
133 }
134 
135 #define FIND(s, k, t, b, e)                                                   \
136 	b = hash((k), (s)->case_sensitive) % (s)->size;                       \
137 	if ((s)->case_sensitive) {                                            \
138 		for (e = HEAD((s)->table[b]); e != NULL; e = NEXT(e, link)) { \
139 			if (((t) == 0 || e->type == (t)) &&                   \
140 			    strcmp(e->key, (k)) == 0)                         \
141 				break;                                        \
142 		}                                                             \
143 	} else {                                                              \
144 		for (e = HEAD((s)->table[b]); e != NULL; e = NEXT(e, link)) { \
145 			if (((t) == 0 || e->type == (t)) &&                   \
146 			    strcasecmp(e->key, (k)) == 0)                     \
147 				break;                                        \
148 		}                                                             \
149 	}
150 
151 isc_result_t
152 isc_symtab_lookup(isc_symtab_t *symtab, const char *key, unsigned int type,
153 		  isc_symvalue_t *value) {
154 	unsigned int bucket;
155 	elt_t *elt;
156 
157 	REQUIRE(VALID_SYMTAB(symtab));
158 	REQUIRE(key != NULL);
159 
160 	FIND(symtab, key, type, bucket, elt);
161 
162 	if (elt == NULL) {
163 		return ISC_R_NOTFOUND;
164 	}
165 
166 	SET_IF_NOT_NULL(value, elt->value);
167 
168 	return ISC_R_SUCCESS;
169 }
170 
171 static void
172 grow_table(isc_symtab_t *symtab) {
173 	eltlist_t *newtable;
174 	unsigned int i, newsize, newmax;
175 
176 	REQUIRE(symtab != NULL);
177 
178 	newsize = symtab->size * 2;
179 	newmax = newsize * 3 / 4;
180 	INSIST(newsize > 0U && newmax > 0U);
181 
182 	newtable = isc_mem_cget(symtab->mctx, newsize, sizeof(eltlist_t));
183 
184 	for (i = 0; i < newsize; i++) {
185 		INIT_LIST(newtable[i]);
186 	}
187 
188 	for (i = 0; i < symtab->size; i++) {
189 		elt_t *elt, *nelt;
190 
191 		for (elt = HEAD(symtab->table[i]); elt != NULL; elt = nelt) {
192 			unsigned int hv;
193 
194 			nelt = NEXT(elt, link);
195 
196 			UNLINK(symtab->table[i], elt, link);
197 			hv = hash(elt->key, symtab->case_sensitive);
198 			APPEND(newtable[hv % newsize], elt, link);
199 		}
200 	}
201 
202 	isc_mem_cput(symtab->mctx, symtab->table, symtab->size,
203 		     sizeof(eltlist_t));
204 
205 	symtab->table = newtable;
206 	symtab->size = newsize;
207 	symtab->maxload = newmax;
208 }
209 
210 isc_result_t
211 isc_symtab_define(isc_symtab_t *symtab, const char *key, unsigned int type,
212 		  isc_symvalue_t value, isc_symexists_t exists_policy) {
213 	unsigned int bucket;
214 	elt_t *elt;
215 
216 	REQUIRE(VALID_SYMTAB(symtab));
217 	REQUIRE(key != NULL);
218 	REQUIRE(type != 0);
219 
220 	FIND(symtab, key, type, bucket, elt);
221 
222 	if (exists_policy != isc_symexists_add && elt != NULL) {
223 		if (exists_policy == isc_symexists_reject) {
224 			return ISC_R_EXISTS;
225 		}
226 		INSIST(exists_policy == isc_symexists_replace);
227 		UNLINK(symtab->table[bucket], elt, link);
228 		if (symtab->undefine_action != NULL) {
229 			(symtab->undefine_action)(elt->key, elt->type,
230 						  elt->value,
231 						  symtab->undefine_arg);
232 		}
233 	} else {
234 		elt = isc_mem_get(symtab->mctx, sizeof(*elt));
235 		ISC_LINK_INIT(elt, link);
236 		symtab->count++;
237 	}
238 
239 	/*
240 	 * Though the "key" can be const coming in, it is not stored as const
241 	 * so that the calling program can easily have writable access to
242 	 * it in its undefine_action function.  In the event that it *was*
243 	 * truly const coming in and then the caller modified it anyway ...
244 	 * well, don't do that!
245 	 */
246 	elt->key = UNCONST(key);
247 	elt->type = type;
248 	elt->value = value;
249 
250 	/*
251 	 * We prepend so that the most recent definition will be found.
252 	 */
253 	PREPEND(symtab->table[bucket], elt, link);
254 
255 	if (symtab->count > symtab->maxload) {
256 		grow_table(symtab);
257 	}
258 
259 	return ISC_R_SUCCESS;
260 }
261 
262 isc_result_t
263 isc_symtab_undefine(isc_symtab_t *symtab, const char *key, unsigned int type) {
264 	unsigned int bucket;
265 	elt_t *elt;
266 
267 	REQUIRE(VALID_SYMTAB(symtab));
268 	REQUIRE(key != NULL);
269 
270 	FIND(symtab, key, type, bucket, elt);
271 
272 	if (elt == NULL) {
273 		return ISC_R_NOTFOUND;
274 	}
275 
276 	if (symtab->undefine_action != NULL) {
277 		(symtab->undefine_action)(elt->key, elt->type, elt->value,
278 					  symtab->undefine_arg);
279 	}
280 	UNLINK(symtab->table[bucket], elt, link);
281 	isc_mem_put(symtab->mctx, elt, sizeof(*elt));
282 	symtab->count--;
283 
284 	return ISC_R_SUCCESS;
285 }
286 
287 unsigned int
288 isc_symtab_count(isc_symtab_t *symtab) {
289 	REQUIRE(VALID_SYMTAB(symtab));
290 	return symtab->count;
291 }
292