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