1 
2 /* Author : Stephen Smalley, <sds@epoch.ncsc.mil> */
3 
4 /* FLASK */
5 
6 /*
7  * Implementation of the hash table type.
8  */
9 
10 #include <stdlib.h>
11 #include <string.h>
12 #include "hashtab.h"
13 
hashtab_create(unsigned int (* hash_value)(hashtab_t h,const hashtab_key_t key),int (* keycmp)(hashtab_t h,const hashtab_key_t key1,const hashtab_key_t key2),unsigned int size)14 hashtab_t hashtab_create(unsigned int (*hash_value) (hashtab_t h,
15 						     const hashtab_key_t key),
16 			 int (*keycmp) (hashtab_t h,
17 					const hashtab_key_t key1,
18 					const hashtab_key_t key2),
19 			 unsigned int size)
20 {
21 
22 	hashtab_t p;
23 	unsigned int i;
24 
25 	p = (hashtab_t) malloc(sizeof(hashtab_val_t));
26 	if (p == NULL)
27 		return p;
28 
29 	memset(p, 0, sizeof(hashtab_val_t));
30 	p->size = size;
31 	p->nel = 0;
32 	p->hash_value = hash_value;
33 	p->keycmp = keycmp;
34 	p->htable = (hashtab_ptr_t *) malloc(sizeof(hashtab_ptr_t) * size);
35 	if (p->htable == NULL) {
36 		free(p);
37 		return NULL;
38 	}
39 	for (i = 0; i < size; i++)
40 		p->htable[i] = (hashtab_ptr_t) NULL;
41 
42 	return p;
43 }
44 
hashtab_insert(hashtab_t h,hashtab_key_t key,hashtab_datum_t datum)45 int hashtab_insert(hashtab_t h, hashtab_key_t key, hashtab_datum_t datum)
46 {
47 	int hvalue;
48 	hashtab_ptr_t prev, cur, newnode;
49 
50 	if (!h)
51 		return HASHTAB_OVERFLOW;
52 
53 	hvalue = h->hash_value(h, key);
54 	prev = NULL;
55 	cur = h->htable[hvalue];
56 	while (cur && h->keycmp(h, key, cur->key) > 0) {
57 		prev = cur;
58 		cur = cur->next;
59 	}
60 
61 	if (cur && (h->keycmp(h, key, cur->key) == 0))
62 		return HASHTAB_PRESENT;
63 
64 	newnode = (hashtab_ptr_t) malloc(sizeof(hashtab_node_t));
65 	if (newnode == NULL)
66 		return HASHTAB_OVERFLOW;
67 	memset(newnode, 0, sizeof(struct hashtab_node));
68 	newnode->key = key;
69 	newnode->datum = datum;
70 	if (prev) {
71 		newnode->next = prev->next;
72 		prev->next = newnode;
73 	} else {
74 		newnode->next = h->htable[hvalue];
75 		h->htable[hvalue] = newnode;
76 	}
77 
78 	h->nel++;
79 	return HASHTAB_SUCCESS;
80 }
81 
hashtab_remove(hashtab_t h,hashtab_key_t key,void (* destroy)(hashtab_key_t k,hashtab_datum_t d,void * args),void * args)82 int hashtab_remove(hashtab_t h, hashtab_key_t key,
83 		   void (*destroy) (hashtab_key_t k,
84 				    hashtab_datum_t d, void *args), void *args)
85 {
86 	int hvalue;
87 	hashtab_ptr_t cur, last;
88 
89 	if (!h)
90 		return HASHTAB_MISSING;
91 
92 	hvalue = h->hash_value(h, key);
93 	last = NULL;
94 	cur = h->htable[hvalue];
95 	while (cur != NULL && h->keycmp(h, key, cur->key) > 0) {
96 		last = cur;
97 		cur = cur->next;
98 	}
99 
100 	if (cur == NULL || (h->keycmp(h, key, cur->key) != 0))
101 		return HASHTAB_MISSING;
102 
103 	if (last == NULL)
104 		h->htable[hvalue] = cur->next;
105 	else
106 		last->next = cur->next;
107 
108 	if (destroy)
109 		destroy(cur->key, cur->datum, args);
110 	free(cur);
111 	h->nel--;
112 	return HASHTAB_SUCCESS;
113 }
114 
hashtab_replace(hashtab_t h,hashtab_key_t key,hashtab_datum_t datum,void (* destroy)(hashtab_key_t k,hashtab_datum_t d,void * args),void * args)115 int hashtab_replace(hashtab_t h, hashtab_key_t key, hashtab_datum_t datum,
116 		    void (*destroy) (hashtab_key_t k,
117 				     hashtab_datum_t d, void *args), void *args)
118 {
119 	int hvalue;
120 	hashtab_ptr_t prev, cur, newnode;
121 
122 	if (!h)
123 		return HASHTAB_OVERFLOW;
124 
125 	hvalue = h->hash_value(h, key);
126 	prev = NULL;
127 	cur = h->htable[hvalue];
128 	while (cur != NULL && h->keycmp(h, key, cur->key) > 0) {
129 		prev = cur;
130 		cur = cur->next;
131 	}
132 
133 	if (cur && (h->keycmp(h, key, cur->key) == 0)) {
134 		if (destroy)
135 			destroy(cur->key, cur->datum, args);
136 		cur->key = key;
137 		cur->datum = datum;
138 	} else {
139 		newnode = (hashtab_ptr_t) malloc(sizeof(hashtab_node_t));
140 		if (newnode == NULL)
141 			return HASHTAB_OVERFLOW;
142 		memset(newnode, 0, sizeof(struct hashtab_node));
143 		newnode->key = key;
144 		newnode->datum = datum;
145 		if (prev) {
146 			newnode->next = prev->next;
147 			prev->next = newnode;
148 		} else {
149 			newnode->next = h->htable[hvalue];
150 			h->htable[hvalue] = newnode;
151 		}
152 	}
153 
154 	return HASHTAB_SUCCESS;
155 }
156 
hashtab_search(hashtab_t h,const hashtab_key_t key)157 hashtab_datum_t hashtab_search(hashtab_t h, const hashtab_key_t key)
158 {
159 
160 	int hvalue;
161 	hashtab_ptr_t cur;
162 
163 	if (!h)
164 		return NULL;
165 
166 	hvalue = h->hash_value(h, key);
167 	cur = h->htable[hvalue];
168 	while (cur != NULL && h->keycmp(h, key, cur->key) > 0)
169 		cur = cur->next;
170 
171 	if (cur == NULL || (h->keycmp(h, key, cur->key) != 0))
172 		return NULL;
173 
174 	return cur->datum;
175 }
176 
hashtab_destroy(hashtab_t h)177 void hashtab_destroy(hashtab_t h)
178 {
179 	unsigned int i;
180 	hashtab_ptr_t cur, temp;
181 
182 	if (!h)
183 		return;
184 
185 	for (i = 0; i < h->size; i++) {
186 		cur = h->htable[i];
187 		while (cur != NULL) {
188 			temp = cur;
189 			cur = cur->next;
190 			free(temp);
191 		}
192 		h->htable[i] = NULL;
193 	}
194 
195 	free(h->htable);
196 	h->htable = NULL;
197 
198 	free(h);
199 }
200 
hashtab_map(hashtab_t h,int (* apply)(hashtab_key_t k,hashtab_datum_t d,void * args),void * args)201 int hashtab_map(hashtab_t h,
202 		int (*apply) (hashtab_key_t k,
203 			      hashtab_datum_t d, void *args), void *args)
204 {
205 	unsigned int i, ret;
206 	hashtab_ptr_t cur;
207 
208 	if (!h)
209 		return HASHTAB_SUCCESS;
210 
211 	for (i = 0; i < h->size; i++) {
212 		cur = h->htable[i];
213 		while (cur != NULL) {
214 			ret = apply(cur->key, cur->datum, args);
215 			if (ret)
216 				return ret;
217 			cur = cur->next;
218 		}
219 	}
220 	return HASHTAB_SUCCESS;
221 }
222 
hashtab_map_remove_on_error(hashtab_t h,int (* apply)(hashtab_key_t k,hashtab_datum_t d,void * args),void (* destroy)(hashtab_key_t k,hashtab_datum_t d,void * args),void * args)223 void hashtab_map_remove_on_error(hashtab_t h,
224 				 int (*apply) (hashtab_key_t k,
225 					       hashtab_datum_t d,
226 					       void *args),
227 				 void (*destroy) (hashtab_key_t k,
228 						  hashtab_datum_t d,
229 						  void *args), void *args)
230 {
231 	unsigned int i;
232 	int ret;
233 	hashtab_ptr_t last, cur, temp;
234 
235 	if (!h)
236 		return;
237 
238 	for (i = 0; i < h->size; i++) {
239 		last = NULL;
240 		cur = h->htable[i];
241 		while (cur != NULL) {
242 			ret = apply(cur->key, cur->datum, args);
243 			if (ret) {
244 				if (last) {
245 					last->next = cur->next;
246 				} else {
247 					h->htable[i] = cur->next;
248 				}
249 
250 				temp = cur;
251 				cur = cur->next;
252 				if (destroy)
253 					destroy(temp->key, temp->datum, args);
254 				free(temp);
255 				h->nel--;
256 			} else {
257 				last = cur;
258 				cur = cur->next;
259 			}
260 		}
261 	}
262 
263 	return;
264 }
265 
hashtab_hash_eval(hashtab_t h,char * tag)266 void hashtab_hash_eval(hashtab_t h, char *tag)
267 {
268 	unsigned int i;
269 	int chain_len, slots_used, max_chain_len;
270 	hashtab_ptr_t cur;
271 
272 	slots_used = 0;
273 	max_chain_len = 0;
274 	for (i = 0; i < h->size; i++) {
275 		cur = h->htable[i];
276 		if (cur) {
277 			slots_used++;
278 			chain_len = 0;
279 			while (cur) {
280 				chain_len++;
281 				cur = cur->next;
282 			}
283 
284 			if (chain_len > max_chain_len)
285 				max_chain_len = chain_len;
286 		}
287 	}
288 
289 	printf
290 	    ("%s:  %d entries and %d/%d buckets used, longest chain length %d\n",
291 	     tag, h->nel, slots_used, h->size, max_chain_len);
292 }
293