/src/p11-kit/common/dict.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2004 Stefan Walter |
3 | | * Copyright (c) 2011 Collabora Ltd. |
4 | | * |
5 | | * Redistribution and use in source and binary forms, with or without |
6 | | * modification, are permitted provided that the following conditions |
7 | | * are met: |
8 | | * |
9 | | * * Redistributions of source code must retain the above |
10 | | * copyright notice, this list of conditions and the |
11 | | * following disclaimer. |
12 | | * * Redistributions in binary form must reproduce the |
13 | | * above copyright notice, this list of conditions and |
14 | | * the following disclaimer in the documentation and/or |
15 | | * other materials provided with the distribution. |
16 | | * * The names of contributors to this software may not be |
17 | | * used to endorse or promote products derived from this |
18 | | * software without specific prior written permission. |
19 | | * |
20 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
21 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
22 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
23 | | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
24 | | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
25 | | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
26 | | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
27 | | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
28 | | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
29 | | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
30 | | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
31 | | * DAMAGE. |
32 | | */ |
33 | | |
34 | | #include "config.h" |
35 | | |
36 | | #include "debug.h" |
37 | | #include "dict.h" |
38 | | #include "hash.h" |
39 | | |
40 | | #include <sys/types.h> |
41 | | |
42 | | #include <assert.h> |
43 | | #include <stdint.h> |
44 | | #include <stdlib.h> |
45 | | #include <string.h> |
46 | | |
47 | | struct _p11_dict { |
48 | | p11_dict_hasher hash_func; |
49 | | p11_dict_equals equal_func; |
50 | | p11_destroyer key_destroy_func; |
51 | | p11_destroyer value_destroy_func; |
52 | | |
53 | | struct _p11_dictbucket **buckets; |
54 | | unsigned int num_items; |
55 | | unsigned int num_buckets; |
56 | | }; |
57 | | |
58 | | typedef struct _p11_dictbucket { |
59 | | void *key; |
60 | | unsigned int hashed; |
61 | | void *value; |
62 | | struct _p11_dictbucket *next; |
63 | | } dictbucket; |
64 | | |
65 | | static dictbucket * |
66 | | next_entry (p11_dictiter *iter) |
67 | 266 | { |
68 | 266 | dictbucket *bucket = iter->next; |
69 | 950 | while (!bucket) { |
70 | 760 | if (iter->index >= iter->dict->num_buckets) |
71 | 76 | return NULL; |
72 | 684 | bucket = iter->dict->buckets[iter->index++]; |
73 | 684 | } |
74 | 190 | iter->next = bucket->next; |
75 | 190 | return bucket; |
76 | 266 | } |
77 | | |
78 | | |
79 | | bool |
80 | | p11_dict_next (p11_dictiter *iter, |
81 | | void **key, |
82 | | void **value) |
83 | 0 | { |
84 | 0 | dictbucket *bucket = next_entry (iter); |
85 | 0 | if (bucket == NULL) |
86 | 0 | return false; |
87 | 0 | if (key) |
88 | 0 | *key = bucket->key; |
89 | 0 | if (value) |
90 | 0 | *value = bucket->value; |
91 | 0 | return true; |
92 | 0 | } |
93 | | |
94 | | void |
95 | | p11_dict_iterate (p11_dict *dict, |
96 | | p11_dictiter *iter) |
97 | 76 | { |
98 | 76 | iter->dict = dict; |
99 | 76 | iter->index = 0; |
100 | 76 | iter->next = NULL; |
101 | 76 | } |
102 | | |
103 | | static dictbucket ** |
104 | | lookup_or_create_bucket (p11_dict *dict, |
105 | | const void *key, |
106 | | bool create) |
107 | 380 | { |
108 | 380 | dictbucket **bucketp; |
109 | 380 | unsigned int hash; |
110 | | |
111 | | /* Perform the hashing */ |
112 | 380 | hash = dict->hash_func (key); |
113 | | |
114 | | /* scan linked list */ |
115 | 380 | for (bucketp = &dict->buckets[hash % dict->num_buckets]; |
116 | 380 | *bucketp != NULL; bucketp = &(*bucketp)->next) { |
117 | 0 | if((*bucketp)->hashed == hash && dict->equal_func ((*bucketp)->key, key)) |
118 | 0 | break; |
119 | 0 | } |
120 | | |
121 | 380 | if ((*bucketp) != NULL || !create) |
122 | 0 | return bucketp; |
123 | | |
124 | | /* add a new entry for non-NULL val */ |
125 | 380 | (*bucketp) = calloc (1, sizeof (dictbucket)); |
126 | | |
127 | 380 | if (*bucketp != NULL) { |
128 | 380 | (*bucketp)->key = (void*)key; |
129 | 380 | (*bucketp)->hashed = hash; |
130 | 380 | dict->num_items++; |
131 | 380 | } |
132 | | |
133 | 380 | return bucketp; |
134 | 380 | } |
135 | | |
136 | | void * |
137 | | p11_dict_get (p11_dict *dict, |
138 | | const void *key) |
139 | 0 | { |
140 | 0 | dictbucket **bucketp; |
141 | |
|
142 | 0 | bucketp = lookup_or_create_bucket (dict, key, false); |
143 | 0 | if (bucketp && *bucketp) |
144 | 0 | return (void*)((*bucketp)->value); |
145 | 0 | else |
146 | 0 | return NULL; |
147 | 0 | } |
148 | | |
149 | | bool |
150 | | p11_dict_set (p11_dict *dict, |
151 | | void *key, |
152 | | void *val) |
153 | 380 | { |
154 | 380 | dictbucket **bucketp; |
155 | 380 | p11_dictiter iter; |
156 | 380 | dictbucket *bucket; |
157 | 380 | dictbucket **new_buckets; |
158 | 380 | unsigned int num_buckets; |
159 | | |
160 | 380 | bucketp = lookup_or_create_bucket (dict, key, true); |
161 | 380 | if(bucketp && *bucketp) { |
162 | | |
163 | | /* Destroy the previous key */ |
164 | 380 | if ((*bucketp)->key && (*bucketp)->key != key && dict->key_destroy_func) |
165 | 0 | dict->key_destroy_func ((*bucketp)->key); |
166 | | |
167 | | /* Destroy the previous value */ |
168 | 380 | if ((*bucketp)->value && (*bucketp)->value != val && dict->value_destroy_func) |
169 | 0 | dict->value_destroy_func ((*bucketp)->value); |
170 | | |
171 | | /* replace entry */ |
172 | 380 | (*bucketp)->key = key; |
173 | 380 | (*bucketp)->value = val; |
174 | | |
175 | | /* check that the collision rate isn't too high */ |
176 | 380 | if (dict->num_items > dict->num_buckets) { |
177 | 0 | num_buckets = dict->num_buckets * 2 + 1; |
178 | 0 | new_buckets = (dictbucket **)calloc (num_buckets, sizeof (dictbucket *)); |
179 | | |
180 | | /* Ignore failures, maybe we can expand later */ |
181 | 0 | if(new_buckets) { |
182 | 0 | p11_dict_iterate (dict, &iter); |
183 | 0 | while ((bucket = next_entry (&iter)) != NULL) { |
184 | 0 | unsigned int i = bucket->hashed % num_buckets; |
185 | 0 | bucket->next = new_buckets[i]; |
186 | 0 | new_buckets[i] = bucket; |
187 | 0 | } |
188 | |
|
189 | 0 | free (dict->buckets); |
190 | 0 | dict->buckets = new_buckets; |
191 | 0 | dict->num_buckets = num_buckets; |
192 | 0 | } |
193 | 0 | } |
194 | | |
195 | 380 | return true; |
196 | 380 | } |
197 | | |
198 | 0 | return_val_if_reached (false); |
199 | 0 | } |
200 | | |
201 | | bool |
202 | | p11_dict_steal (p11_dict *dict, |
203 | | const void *key, |
204 | | void **stolen_key, |
205 | | void **stolen_value) |
206 | 0 | { |
207 | 0 | dictbucket **bucketp; |
208 | |
|
209 | 0 | bucketp = lookup_or_create_bucket (dict, key, false); |
210 | 0 | if (bucketp && *bucketp) { |
211 | 0 | dictbucket *old = *bucketp; |
212 | 0 | *bucketp = (*bucketp)->next; |
213 | 0 | --dict->num_items; |
214 | 0 | if (stolen_key) |
215 | 0 | *stolen_key = old->key; |
216 | 0 | if (stolen_value) |
217 | 0 | *stolen_value = old->value; |
218 | 0 | free (old); |
219 | 0 | return true; |
220 | 0 | } |
221 | | |
222 | 0 | return false; |
223 | |
|
224 | 0 | } |
225 | | |
226 | | bool |
227 | | p11_dict_remove (p11_dict *dict, |
228 | | const void *key) |
229 | 0 | { |
230 | 0 | void *old_key; |
231 | 0 | void *old_value; |
232 | |
|
233 | 0 | if (!p11_dict_steal (dict, key, &old_key, &old_value)) |
234 | 0 | return false; |
235 | | |
236 | 0 | if (dict->key_destroy_func) |
237 | 0 | dict->key_destroy_func (old_key); |
238 | 0 | if (dict->value_destroy_func) |
239 | 0 | dict->value_destroy_func (old_value); |
240 | 0 | return true; |
241 | 0 | } |
242 | | |
243 | | void |
244 | | p11_dict_clear (p11_dict *dict) |
245 | 76 | { |
246 | 76 | dictbucket *bucket, *next; |
247 | 76 | unsigned int i; |
248 | | |
249 | | /* Free all entries in the array */ |
250 | 760 | for (i = 0; i < dict->num_buckets; ++i) { |
251 | 684 | bucket = dict->buckets[i]; |
252 | 869 | while (bucket != NULL) { |
253 | 185 | next = bucket->next; |
254 | 185 | if (dict->key_destroy_func) |
255 | 0 | dict->key_destroy_func (bucket->key); |
256 | 185 | if (dict->value_destroy_func) |
257 | 185 | dict->value_destroy_func (bucket->value); |
258 | 185 | free (bucket); |
259 | 185 | bucket = next; |
260 | 185 | } |
261 | 684 | } |
262 | | |
263 | 76 | memset (dict->buckets, 0, dict->num_buckets * sizeof (dictbucket *)); |
264 | 76 | dict->num_items = 0; |
265 | 76 | } |
266 | | |
267 | | p11_dict * |
268 | | p11_dict_new (p11_dict_hasher hash_func, |
269 | | p11_dict_equals equal_func, |
270 | | p11_destroyer key_destroy_func, |
271 | | p11_destroyer value_destroy_func) |
272 | 77 | { |
273 | 77 | p11_dict *dict; |
274 | | |
275 | 77 | assert (hash_func); |
276 | 77 | assert (equal_func); |
277 | | |
278 | 77 | dict = malloc (sizeof (p11_dict)); |
279 | 77 | if (dict) { |
280 | 77 | dict->hash_func = hash_func; |
281 | 77 | dict->equal_func = equal_func; |
282 | 77 | dict->key_destroy_func = key_destroy_func; |
283 | 77 | dict->value_destroy_func = value_destroy_func; |
284 | | |
285 | 77 | dict->num_buckets = 9; |
286 | 77 | dict->buckets = (dictbucket **)calloc (dict->num_buckets, sizeof (dictbucket *)); |
287 | 77 | if (!dict->buckets) { |
288 | 0 | free (dict); |
289 | 0 | return NULL; |
290 | 0 | } |
291 | | |
292 | 77 | dict->num_items = 0; |
293 | 77 | } |
294 | | |
295 | 77 | return dict; |
296 | 77 | } |
297 | | |
298 | | void |
299 | | p11_dict_free (p11_dict *dict) |
300 | 76 | { |
301 | 76 | dictbucket *bucket; |
302 | 76 | p11_dictiter iter; |
303 | | |
304 | 76 | if (!dict) |
305 | 0 | return; |
306 | | |
307 | 76 | p11_dict_iterate (dict, &iter); |
308 | 266 | while ((bucket = next_entry (&iter)) != NULL) { |
309 | 190 | if (dict->key_destroy_func) |
310 | 0 | dict->key_destroy_func (bucket->key); |
311 | 190 | if (dict->value_destroy_func) |
312 | 190 | dict->value_destroy_func (bucket->value); |
313 | 190 | free (bucket); |
314 | 190 | } |
315 | | |
316 | 76 | if (dict->buckets) |
317 | 76 | free (dict->buckets); |
318 | | |
319 | 76 | free (dict); |
320 | 76 | } |
321 | | |
322 | | unsigned int |
323 | | p11_dict_size (p11_dict *dict) |
324 | 0 | { |
325 | 0 | return dict->num_items; |
326 | 0 | } |
327 | | |
328 | | unsigned int |
329 | | p11_dict_str_hash (const void *string) |
330 | 0 | { |
331 | 0 | uint32_t hash; |
332 | 0 | p11_hash_murmur3 (&hash, string, strlen (string), NULL); |
333 | 0 | return hash; |
334 | 0 | } |
335 | | |
336 | | bool |
337 | | p11_dict_str_equal (const void *string_one, |
338 | | const void *string_two) |
339 | 0 | { |
340 | 0 | assert (string_one); |
341 | 0 | assert (string_two); |
342 | | |
343 | 0 | return strcmp (string_one, string_two) == 0; |
344 | 0 | } |
345 | | |
346 | | unsigned int |
347 | | p11_dict_ulongptr_hash (const void *to_ulong) |
348 | 0 | { |
349 | 0 | assert (to_ulong); |
350 | 0 | return (unsigned int)*((unsigned long*)to_ulong); |
351 | 0 | } |
352 | | |
353 | | bool |
354 | | p11_dict_ulongptr_equal (const void *ulong_one, |
355 | | const void *ulong_two) |
356 | 0 | { |
357 | 0 | assert (ulong_one); |
358 | 0 | assert (ulong_two); |
359 | 0 | return *((unsigned long*)ulong_one) == *((unsigned long*)ulong_two); |
360 | 0 | } |
361 | | |
362 | | unsigned int |
363 | | p11_dict_intptr_hash (const void *to_int) |
364 | 0 | { |
365 | 0 | assert (to_int); |
366 | 0 | return (unsigned int)*((int*)to_int); |
367 | 0 | } |
368 | | |
369 | | bool |
370 | | p11_dict_intptr_equal (const void *int_one, |
371 | | const void *int_two) |
372 | 0 | { |
373 | 0 | assert (int_one); |
374 | 0 | assert (int_two); |
375 | 0 | return *((int*)int_one) == *((int*)int_two); |
376 | 0 | } |
377 | | |
378 | | unsigned int |
379 | | p11_dict_direct_hash (const void *ptr) |
380 | 380 | { |
381 | 380 | return (unsigned int)(size_t)ptr; |
382 | 380 | } |
383 | | |
384 | | bool |
385 | | p11_dict_direct_equal (const void *ptr_one, |
386 | | const void *ptr_two) |
387 | 0 | { |
388 | 0 | return ptr_one == ptr_two; |
389 | 0 | } |