xref: /netbsd-src/external/mpl/bind/dist/lib/isccc/symtab.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: symtab.c,v 1.9 2025/01/26 16:25:44 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0 AND ISC
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 /*
17  * Copyright (C) 2001 Nominum, Inc.
18  *
19  * Permission to use, copy, modify, and/or distribute this software for any
20  * purpose with or without fee is hereby granted, provided that the above
21  * copyright notice and this permission notice appear in all copies.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL
24  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY
26  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
27  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
28  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
29  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30  */
31 
32 /*! \file */
33 
34 #include <stdbool.h>
35 #include <stdlib.h>
36 
37 #include <isc/ascii.h>
38 #include <isc/assertions.h>
39 #include <isc/magic.h>
40 #include <isc/result.h>
41 #include <isc/string.h>
42 
43 #include <isccc/symtab.h>
44 #include <isccc/util.h>
45 
46 typedef struct elt {
47 	char *key;
48 	unsigned int type;
49 	isccc_symvalue_t value;
50 	ISC_LINK(struct elt) link;
51 } elt_t;
52 
53 typedef ISC_LIST(elt_t) eltlist_t;
54 
55 #define SYMTAB_MAGIC	 ISC_MAGIC('S', 'y', 'm', 'T')
56 #define VALID_SYMTAB(st) ISC_MAGIC_VALID(st, SYMTAB_MAGIC)
57 
58 struct isccc_symtab {
59 	unsigned int magic;
60 	unsigned int size;
61 	eltlist_t *table;
62 	isccc_symtabundefaction_t undefine_action;
63 	void *undefine_arg;
64 	bool case_sensitive;
65 };
66 
67 isc_result_t
68 isccc_symtab_create(unsigned int size,
69 		    isccc_symtabundefaction_t undefine_action,
70 		    void *undefine_arg, bool case_sensitive,
71 		    isccc_symtab_t **symtabp) {
72 	isccc_symtab_t *symtab;
73 	unsigned int i;
74 
75 	REQUIRE(symtabp != NULL && *symtabp == NULL);
76 	REQUIRE(size > 0); /* Should be prime. */
77 
78 	symtab = malloc(sizeof(*symtab));
79 	if (symtab == NULL) {
80 		return ISC_R_NOMEMORY;
81 	}
82 	symtab->table = calloc(size, sizeof(eltlist_t));
83 	if (symtab->table == NULL) {
84 		free(symtab);
85 		return ISC_R_NOMEMORY;
86 	}
87 	for (i = 0; i < size; i++) {
88 		ISC_LIST_INIT(symtab->table[i]);
89 	}
90 	symtab->size = size;
91 	symtab->undefine_action = undefine_action;
92 	symtab->undefine_arg = undefine_arg;
93 	symtab->case_sensitive = case_sensitive;
94 	symtab->magic = SYMTAB_MAGIC;
95 
96 	*symtabp = symtab;
97 
98 	return ISC_R_SUCCESS;
99 }
100 
101 static void
102 free_elt(isccc_symtab_t *symtab, unsigned int bucket, elt_t *elt) {
103 	ISC_LIST_UNLINK(symtab->table[bucket], elt, link);
104 	if (symtab->undefine_action != NULL) {
105 		(symtab->undefine_action)(elt->key, elt->type, elt->value,
106 					  symtab->undefine_arg);
107 	}
108 	free(elt);
109 }
110 
111 void
112 isccc_symtab_destroy(isccc_symtab_t **symtabp) {
113 	isccc_symtab_t *symtab;
114 	unsigned int i;
115 	elt_t *elt, *nelt;
116 
117 	REQUIRE(symtabp != NULL);
118 	symtab = *symtabp;
119 	*symtabp = NULL;
120 	REQUIRE(VALID_SYMTAB(symtab));
121 
122 	for (i = 0; i < symtab->size; i++) {
123 		for (elt = ISC_LIST_HEAD(symtab->table[i]); elt != NULL;
124 		     elt = nelt)
125 		{
126 			nelt = ISC_LIST_NEXT(elt, link);
127 			free_elt(symtab, i, elt);
128 		}
129 	}
130 	free(symtab->table);
131 	symtab->magic = 0;
132 	free(symtab);
133 }
134 
135 static unsigned int
136 hash(const char *key, bool case_sensitive) {
137 	const char *s;
138 	unsigned int h = 0;
139 	unsigned int g;
140 
141 	/*
142 	 * P. J. Weinberger's hash function, adapted from p. 436 of
143 	 * _Compilers: Principles, Techniques, and Tools_, Aho, Sethi
144 	 * and Ullman, Addison-Wesley, 1986, ISBN 0-201-10088-6.
145 	 */
146 
147 	if (case_sensitive) {
148 		for (s = key; *s != '\0'; s++) {
149 			h = (h << 4) + *s;
150 			if ((g = (h & 0xf0000000)) != 0) {
151 				h = h ^ (g >> 24);
152 				h = h ^ g;
153 			}
154 		}
155 	} else {
156 		for (s = key; *s != '\0'; s++) {
157 			h = (h << 4) + isc_ascii_tolower(*s);
158 			if ((g = (h & 0xf0000000)) != 0) {
159 				h = h ^ (g >> 24);
160 				h = h ^ g;
161 			}
162 		}
163 	}
164 
165 	return h;
166 }
167 
168 #define FIND(s, k, t, b, e)                                       \
169 	b = hash((k), (s)->case_sensitive) % (s)->size;           \
170 	if ((s)->case_sensitive) {                                \
171 		for (e = ISC_LIST_HEAD((s)->table[b]); e != NULL; \
172 		     e = ISC_LIST_NEXT(e, link))                  \
173 		{                                                 \
174 			if (((t) == 0 || e->type == (t)) &&       \
175 			    strcmp(e->key, (k)) == 0)             \
176 				break;                            \
177 		}                                                 \
178 	} else {                                                  \
179 		for (e = ISC_LIST_HEAD((s)->table[b]); e != NULL; \
180 		     e = ISC_LIST_NEXT(e, link))                  \
181 		{                                                 \
182 			if (((t) == 0 || e->type == (t)) &&       \
183 			    strcasecmp(e->key, (k)) == 0)         \
184 				break;                            \
185 		}                                                 \
186 	}
187 
188 isc_result_t
189 isccc_symtab_lookup(isccc_symtab_t *symtab, const char *key, unsigned int type,
190 		    isccc_symvalue_t *value) {
191 	unsigned int bucket;
192 	elt_t *elt;
193 
194 	REQUIRE(VALID_SYMTAB(symtab));
195 	REQUIRE(key != NULL);
196 
197 	FIND(symtab, key, type, bucket, elt);
198 
199 	if (elt == NULL) {
200 		return ISC_R_NOTFOUND;
201 	}
202 
203 	SET_IF_NOT_NULL(value, elt->value);
204 
205 	return ISC_R_SUCCESS;
206 }
207 
208 isc_result_t
209 isccc_symtab_define(isccc_symtab_t *symtab, char *key, unsigned int type,
210 		    isccc_symvalue_t value, isccc_symexists_t exists_policy) {
211 	unsigned int bucket;
212 	elt_t *elt;
213 
214 	REQUIRE(VALID_SYMTAB(symtab));
215 	REQUIRE(key != NULL);
216 	REQUIRE(type != 0);
217 
218 	FIND(symtab, key, type, bucket, elt);
219 
220 	if (exists_policy != isccc_symexists_add && elt != NULL) {
221 		if (exists_policy == isccc_symexists_reject) {
222 			return ISC_R_EXISTS;
223 		}
224 		INSIST(exists_policy == isccc_symexists_replace);
225 		ISC_LIST_UNLINK(symtab->table[bucket], elt, link);
226 		if (symtab->undefine_action != NULL) {
227 			(symtab->undefine_action)(elt->key, elt->type,
228 						  elt->value,
229 						  symtab->undefine_arg);
230 		}
231 	} else {
232 		elt = malloc(sizeof(*elt));
233 		if (elt == NULL) {
234 			return ISC_R_NOMEMORY;
235 		}
236 		ISC_LINK_INIT(elt, link);
237 	}
238 
239 	elt->key = key;
240 	elt->type = type;
241 	elt->value = value;
242 
243 	/*
244 	 * We prepend so that the most recent definition will be found.
245 	 */
246 	ISC_LIST_PREPEND(symtab->table[bucket], elt, link);
247 
248 	return ISC_R_SUCCESS;
249 }
250 
251 isc_result_t
252 isccc_symtab_undefine(isccc_symtab_t *symtab, const char *key,
253 		      unsigned int type) {
254 	unsigned int bucket;
255 	elt_t *elt;
256 
257 	REQUIRE(VALID_SYMTAB(symtab));
258 	REQUIRE(key != NULL);
259 
260 	FIND(symtab, key, type, bucket, elt);
261 
262 	if (elt == NULL) {
263 		return ISC_R_NOTFOUND;
264 	}
265 
266 	free_elt(symtab, bucket, elt);
267 
268 	return ISC_R_SUCCESS;
269 }
270 
271 void
272 isccc_symtab_foreach(isccc_symtab_t *symtab, isccc_symtabforeachaction_t action,
273 		     void *arg) {
274 	unsigned int i;
275 	elt_t *elt, *nelt;
276 
277 	REQUIRE(VALID_SYMTAB(symtab));
278 	REQUIRE(action != NULL);
279 
280 	for (i = 0; i < symtab->size; i++) {
281 		for (elt = ISC_LIST_HEAD(symtab->table[i]); elt != NULL;
282 		     elt = nelt)
283 		{
284 			nelt = ISC_LIST_NEXT(elt, link);
285 			if ((action)(elt->key, elt->type, elt->value, arg)) {
286 				free_elt(symtab, i, elt);
287 			}
288 		}
289 	}
290 }
291