1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25 #include "curl_setup.h"
26
27 #include <curl/curl.h>
28
29 #include "hash.h"
30 #include "llist.h"
31 #include "curl_memory.h"
32
33 /* The last #include file should be: */
34 #include "memdebug.h"
35
36 /* random patterns for API verification */
37 #define HASHINIT 0x7017e781
38 #define ITERINIT 0x5FEDCBA9
39
40 static void
hash_element_dtor(void * user,void * element)41 hash_element_dtor(void *user, void *element)
42 {
43 struct Curl_hash *h = (struct Curl_hash *) user;
44 struct Curl_hash_element *e = (struct Curl_hash_element *) element;
45
46 if(e->ptr) {
47 if(e->dtor)
48 e->dtor(e->key, e->key_len, e->ptr);
49 else
50 h->dtor(e->ptr);
51 e->ptr = NULL;
52 }
53
54 e->key_len = 0;
55
56 free(e);
57 }
58
59 /* Initializes a hash structure.
60 * Return 1 on error, 0 is fine.
61 *
62 * @unittest: 1602
63 * @unittest: 1603
64 */
65 void
Curl_hash_init(struct Curl_hash * h,size_t slots,hash_function hfunc,comp_function comparator,Curl_hash_dtor dtor)66 Curl_hash_init(struct Curl_hash *h,
67 size_t slots,
68 hash_function hfunc,
69 comp_function comparator,
70 Curl_hash_dtor dtor)
71 {
72 DEBUGASSERT(h);
73 DEBUGASSERT(slots);
74 DEBUGASSERT(hfunc);
75 DEBUGASSERT(comparator);
76 DEBUGASSERT(dtor);
77
78 h->table = NULL;
79 h->hash_func = hfunc;
80 h->comp_func = comparator;
81 h->dtor = dtor;
82 h->size = 0;
83 h->slots = slots;
84 #ifdef DEBUGBUILD
85 h->init = HASHINIT;
86 #endif
87 }
88
89 static struct Curl_hash_element *
mk_hash_element(const void * key,size_t key_len,const void * p,Curl_hash_elem_dtor dtor)90 mk_hash_element(const void *key, size_t key_len, const void *p,
91 Curl_hash_elem_dtor dtor)
92 {
93 /* allocate the struct plus memory after it to store the key */
94 struct Curl_hash_element *he = malloc(sizeof(struct Curl_hash_element) +
95 key_len);
96 if(he) {
97 /* copy the key */
98 memcpy(he->key, key, key_len);
99 he->key_len = key_len;
100 he->ptr = (void *) p;
101 he->dtor = dtor;
102 }
103 return he;
104 }
105
106 #define FETCH_LIST(x,y,z) &x->table[x->hash_func(y, z, x->slots)]
107
Curl_hash_add2(struct Curl_hash * h,void * key,size_t key_len,void * p,Curl_hash_elem_dtor dtor)108 void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p,
109 Curl_hash_elem_dtor dtor)
110 {
111 struct Curl_hash_element *he;
112 struct Curl_llist_node *le;
113 struct Curl_llist *l;
114
115 DEBUGASSERT(h);
116 DEBUGASSERT(h->slots);
117 DEBUGASSERT(h->init == HASHINIT);
118 if(!h->table) {
119 size_t i;
120 h->table = malloc(h->slots * sizeof(struct Curl_llist));
121 if(!h->table)
122 return NULL; /* OOM */
123 for(i = 0; i < h->slots; ++i)
124 Curl_llist_init(&h->table[i], hash_element_dtor);
125 }
126
127 l = FETCH_LIST(h, key, key_len);
128
129 for(le = Curl_llist_head(l); le; le = Curl_node_next(le)) {
130 he = (struct Curl_hash_element *) Curl_node_elem(le);
131 if(h->comp_func(he->key, he->key_len, key, key_len)) {
132 Curl_node_uremove(le, (void *)h);
133 --h->size;
134 break;
135 }
136 }
137
138 he = mk_hash_element(key, key_len, p, dtor);
139 if(he) {
140 Curl_llist_append(l, he, &he->list);
141 ++h->size;
142 return p; /* return the new entry */
143 }
144
145 return NULL; /* failure */
146 }
147
148 /* Insert the data in the hash. If there already was a match in the hash, that
149 * data is replaced. This function also "lazily" allocates the table if
150 * needed, as it is not done in the _init function (anymore).
151 *
152 * @unittest: 1305
153 * @unittest: 1602
154 * @unittest: 1603
155 */
156 void *
Curl_hash_add(struct Curl_hash * h,void * key,size_t key_len,void * p)157 Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p)
158 {
159 return Curl_hash_add2(h, key, key_len, p, NULL);
160 }
161
162 /* Remove the identified hash entry.
163 * Returns non-zero on failure.
164 *
165 * @unittest: 1603
166 */
Curl_hash_delete(struct Curl_hash * h,void * key,size_t key_len)167 int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len)
168 {
169 DEBUGASSERT(h);
170 DEBUGASSERT(h->slots);
171 DEBUGASSERT(h->init == HASHINIT);
172 if(h->table) {
173 struct Curl_llist_node *le;
174 struct Curl_llist *l = FETCH_LIST(h, key, key_len);
175
176 for(le = Curl_llist_head(l); le; le = Curl_node_next(le)) {
177 struct Curl_hash_element *he = Curl_node_elem(le);
178 if(h->comp_func(he->key, he->key_len, key, key_len)) {
179 Curl_node_uremove(le, (void *) h);
180 --h->size;
181 return 0;
182 }
183 }
184 }
185 return 1;
186 }
187
188 /* Retrieves a hash element.
189 *
190 * @unittest: 1603
191 */
192 void *
Curl_hash_pick(struct Curl_hash * h,void * key,size_t key_len)193 Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len)
194 {
195 DEBUGASSERT(h);
196 DEBUGASSERT(h->init == HASHINIT);
197 if(h->table) {
198 struct Curl_llist_node *le;
199 struct Curl_llist *l;
200 DEBUGASSERT(h->slots);
201 l = FETCH_LIST(h, key, key_len);
202 for(le = Curl_llist_head(l); le; le = Curl_node_next(le)) {
203 struct Curl_hash_element *he = Curl_node_elem(le);
204 if(h->comp_func(he->key, he->key_len, key, key_len)) {
205 return he->ptr;
206 }
207 }
208 }
209
210 return NULL;
211 }
212
213 /* Destroys all the entries in the given hash and resets its attributes,
214 * prepping the given hash for [static|dynamic] deallocation.
215 *
216 * @unittest: 1305
217 * @unittest: 1602
218 * @unittest: 1603
219 */
220 void
Curl_hash_destroy(struct Curl_hash * h)221 Curl_hash_destroy(struct Curl_hash *h)
222 {
223 DEBUGASSERT(h->init == HASHINIT);
224 if(h->table) {
225 size_t i;
226 for(i = 0; i < h->slots; ++i) {
227 Curl_llist_destroy(&h->table[i], (void *) h);
228 }
229 Curl_safefree(h->table);
230 }
231 h->size = 0;
232 h->slots = 0;
233 }
234
235 /* Removes all the entries in the given hash.
236 *
237 * @unittest: 1602
238 */
239 void
Curl_hash_clean(struct Curl_hash * h)240 Curl_hash_clean(struct Curl_hash *h)
241 {
242 Curl_hash_clean_with_criterium(h, NULL, NULL);
243 }
244
Curl_hash_count(struct Curl_hash * h)245 size_t Curl_hash_count(struct Curl_hash *h)
246 {
247 DEBUGASSERT(h->init == HASHINIT);
248 return h->size;
249 }
250
251 /* Cleans all entries that pass the comp function criteria. */
252 void
Curl_hash_clean_with_criterium(struct Curl_hash * h,void * user,int (* comp)(void *,void *))253 Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user,
254 int (*comp)(void *, void *))
255 {
256 size_t i;
257
258 if(!h || !h->table)
259 return;
260
261 DEBUGASSERT(h->init == HASHINIT);
262 for(i = 0; i < h->slots; ++i) {
263 struct Curl_llist *list = &h->table[i];
264 struct Curl_llist_node *le =
265 Curl_llist_head(list); /* get first list entry */
266 while(le) {
267 struct Curl_hash_element *he = Curl_node_elem(le);
268 struct Curl_llist_node *lnext = Curl_node_next(le);
269 /* ask the callback function if we shall remove this entry or not */
270 if(!comp || comp(user, he->ptr)) {
271 Curl_node_uremove(le, (void *) h);
272 --h->size; /* one less entry in the hash now */
273 }
274 le = lnext;
275 }
276 }
277 }
278
Curl_hash_str(void * key,size_t key_length,size_t slots_num)279 size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num)
280 {
281 const char *key_str = (const char *) key;
282 const char *end = key_str + key_length;
283 size_t h = 5381;
284
285 while(key_str < end) {
286 size_t j = (size_t)*key_str++;
287 h += h << 5;
288 h ^= j;
289 }
290
291 return (h % slots_num);
292 }
293
Curl_str_key_compare(void * k1,size_t key1_len,void * k2,size_t key2_len)294 size_t Curl_str_key_compare(void *k1, size_t key1_len,
295 void *k2, size_t key2_len)
296 {
297 if((key1_len == key2_len) && !memcmp(k1, k2, key1_len))
298 return 1;
299
300 return 0;
301 }
302
Curl_hash_start_iterate(struct Curl_hash * hash,struct Curl_hash_iterator * iter)303 void Curl_hash_start_iterate(struct Curl_hash *hash,
304 struct Curl_hash_iterator *iter)
305 {
306 DEBUGASSERT(hash->init == HASHINIT);
307 iter->hash = hash;
308 iter->slot_index = 0;
309 iter->current_element = NULL;
310 #ifdef DEBUGBUILD
311 iter->init = ITERINIT;
312 #endif
313 }
314
315 struct Curl_hash_element *
Curl_hash_next_element(struct Curl_hash_iterator * iter)316 Curl_hash_next_element(struct Curl_hash_iterator *iter)
317 {
318 struct Curl_hash *h;
319 DEBUGASSERT(iter->init == ITERINIT);
320 h = iter->hash;
321 if(!h->table)
322 return NULL; /* empty hash, nothing to return */
323
324 /* Get the next element in the current list, if any */
325 if(iter->current_element)
326 iter->current_element = Curl_node_next(iter->current_element);
327
328 /* If we have reached the end of the list, find the next one */
329 if(!iter->current_element) {
330 size_t i;
331 for(i = iter->slot_index; i < h->slots; i++) {
332 if(Curl_llist_head(&h->table[i])) {
333 iter->current_element = Curl_llist_head(&h->table[i]);
334 iter->slot_index = i + 1;
335 break;
336 }
337 }
338 }
339
340 if(iter->current_element) {
341 struct Curl_hash_element *he = Curl_node_elem(iter->current_element);
342 return he;
343 }
344 return NULL;
345 }
346
347 #if 0 /* useful function for debugging hashes and their contents */
348 void Curl_hash_print(struct Curl_hash *h,
349 void (*func)(void *))
350 {
351 struct Curl_hash_iterator iter;
352 struct Curl_hash_element *he;
353 size_t last_index = ~0;
354
355 if(!h)
356 return;
357
358 fprintf(stderr, "=Hash dump=\n");
359
360 Curl_hash_start_iterate(h, &iter);
361
362 he = Curl_hash_next_element(&iter);
363 while(he) {
364 if(iter.slot_index != last_index) {
365 fprintf(stderr, "index %d:", iter.slot_index);
366 if(last_index != ~0) {
367 fprintf(stderr, "\n");
368 }
369 last_index = iter.slot_index;
370 }
371
372 if(func)
373 func(he->ptr);
374 else
375 fprintf(stderr, " [%p]", (void *)he->ptr);
376
377 he = Curl_hash_next_element(&iter);
378 }
379 fprintf(stderr, "\n");
380 }
381 #endif
382
Curl_hash_offt_init(struct Curl_hash * h,size_t slots,Curl_hash_dtor dtor)383 void Curl_hash_offt_init(struct Curl_hash *h,
384 size_t slots,
385 Curl_hash_dtor dtor)
386 {
387 Curl_hash_init(h, slots, Curl_hash_str, Curl_str_key_compare, dtor);
388 }
389
Curl_hash_offt_set(struct Curl_hash * h,curl_off_t id,void * elem)390 void *Curl_hash_offt_set(struct Curl_hash *h, curl_off_t id, void *elem)
391 {
392 return Curl_hash_add(h, &id, sizeof(id), elem);
393 }
394
Curl_hash_offt_remove(struct Curl_hash * h,curl_off_t id)395 int Curl_hash_offt_remove(struct Curl_hash *h, curl_off_t id)
396 {
397 return Curl_hash_delete(h, &id, sizeof(id));
398 }
399
Curl_hash_offt_get(struct Curl_hash * h,curl_off_t id)400 void *Curl_hash_offt_get(struct Curl_hash *h, curl_off_t id)
401 {
402 return Curl_hash_pick(h, &id, sizeof(id));
403 }
404