Coverage Report

Created: 2025-12-03 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/hash.c
Line
Count
Source
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
32
/* random patterns for API verification */
33
#ifdef DEBUGBUILD
34
6.06k
#define HASHINIT 0x7017e781
35
0
#define ITERINIT 0x5FEDCBA9
36
#endif
37
38
39
#if 0 /* useful function for debugging hashes and their contents */
40
void Curl_hash_print(struct Curl_hash *h,
41
                     void (*func)(void *))
42
{
43
  struct Curl_hash_iterator iter;
44
  struct Curl_hash_element *he;
45
  size_t last_index = UINT_MAX;
46
47
  if(!h)
48
    return;
49
50
  curl_mfprintf(stderr, "=Hash dump=\n");
51
52
  Curl_hash_start_iterate(h, &iter);
53
54
  he = Curl_hash_next_element(&iter);
55
  while(he) {
56
    if(iter.slot_index != last_index) {
57
      curl_mfprintf(stderr, "index %d:", (int)iter.slot_index);
58
      if(last_index != UINT_MAX) {
59
        curl_mfprintf(stderr, "\n");
60
      }
61
      last_index = iter.slot_index;
62
    }
63
64
    if(func)
65
      func(he->ptr);
66
    else
67
      curl_mfprintf(stderr, " [key=%.*s, he=%p, ptr=%p]",
68
                    (int)he->key_len, (char *)he->key,
69
                    (void *)he, (void *)he->ptr);
70
71
    he = Curl_hash_next_element(&iter);
72
  }
73
  curl_mfprintf(stderr, "\n");
74
}
75
#endif
76
77
/* Initializes a hash structure.
78
 * Return 1 on error, 0 is fine.
79
 *
80
 * @unittest: 1602
81
 * @unittest: 1603
82
 */
83
void
84
Curl_hash_init(struct Curl_hash *h,
85
               size_t slots,
86
               hash_function hfunc,
87
               comp_function comparator,
88
               Curl_hash_dtor dtor)
89
6.06k
{
90
6.06k
  DEBUGASSERT(h);
91
6.06k
  DEBUGASSERT(slots);
92
6.06k
  DEBUGASSERT(hfunc);
93
6.06k
  DEBUGASSERT(comparator);
94
6.06k
  DEBUGASSERT(dtor);
95
96
6.06k
  h->table = NULL;
97
6.06k
  h->hash_func = hfunc;
98
6.06k
  h->comp_func = comparator;
99
6.06k
  h->dtor = dtor;
100
6.06k
  h->size = 0;
101
6.06k
  h->slots = slots;
102
6.06k
#ifdef DEBUGBUILD
103
6.06k
  h->init = HASHINIT;
104
6.06k
#endif
105
6.06k
}
106
107
static struct Curl_hash_element *
108
hash_elem_create(const void *key, size_t key_len, const void *p,
109
                 Curl_hash_elem_dtor dtor)
110
0
{
111
0
  struct Curl_hash_element *he;
112
113
  /* allocate the struct plus memory after it to store the key */
114
0
  he = curlx_malloc(sizeof(struct Curl_hash_element) + key_len);
115
0
  if(he) {
116
0
    he->next = NULL;
117
    /* copy the key */
118
0
    memcpy(he->key, key, key_len);
119
0
    he->key_len = key_len;
120
0
    he->ptr = CURL_UNCONST(p);
121
0
    he->dtor = dtor;
122
0
  }
123
0
  return he;
124
0
}
125
126
static void hash_elem_clear_ptr(struct Curl_hash *h,
127
                                struct Curl_hash_element *he)
128
0
{
129
0
  DEBUGASSERT(h);
130
0
  DEBUGASSERT(he);
131
0
  if(he->ptr) {
132
0
    if(he->dtor)
133
0
      he->dtor(he->key, he->key_len, he->ptr);
134
0
    else
135
0
      h->dtor(he->ptr);
136
0
    he->ptr = NULL;
137
0
  }
138
0
}
139
140
static void hash_elem_destroy(struct Curl_hash *h,
141
                              struct Curl_hash_element *he)
142
0
{
143
0
  hash_elem_clear_ptr(h, he);
144
0
  curlx_free(he);
145
0
}
146
147
static void hash_elem_unlink(struct Curl_hash *h,
148
                             struct Curl_hash_element **he_anchor,
149
                             struct Curl_hash_element *he)
150
0
{
151
0
  *he_anchor = he->next;
152
0
  --h->size;
153
0
}
154
155
static void hash_elem_link(struct Curl_hash *h,
156
                           struct Curl_hash_element **he_anchor,
157
                           struct Curl_hash_element *he)
158
0
{
159
0
  he->next = *he_anchor;
160
0
  *he_anchor = he;
161
0
  ++h->size;
162
0
}
163
164
0
#define CURL_HASH_SLOT(x,y,z)      x->table[x->hash_func(y, z, x->slots)]
165
0
#define CURL_HASH_SLOT_ADDR(x,y,z) &CURL_HASH_SLOT(x,y,z)
166
167
void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p,
168
                     Curl_hash_elem_dtor dtor)
169
0
{
170
0
  struct Curl_hash_element *he, **slot;
171
172
0
  DEBUGASSERT(h);
173
0
  DEBUGASSERT(h->slots);
174
0
  DEBUGASSERT(h->init == HASHINIT);
175
0
  if(!h->table) {
176
0
    h->table = curlx_calloc(h->slots, sizeof(struct Curl_hash_element *));
177
0
    if(!h->table)
178
0
      return NULL; /* OOM */
179
0
  }
180
181
0
  slot = CURL_HASH_SLOT_ADDR(h, key, key_len);
182
0
  for(he = *slot; he; he = he->next) {
183
0
    if(h->comp_func(he->key, he->key_len, key, key_len)) {
184
      /* existing key entry, overwrite by clearing old pointer */
185
0
      hash_elem_clear_ptr(h, he);
186
0
      he->ptr = (void *)p;
187
0
      he->dtor = dtor;
188
0
      return p;
189
0
    }
190
0
  }
191
192
0
  he = hash_elem_create(key, key_len, p, dtor);
193
0
  if(!he)
194
0
    return NULL; /* OOM */
195
196
0
  hash_elem_link(h, slot, he);
197
0
  return p; /* return the new entry */
198
0
}
199
200
/* Insert the data in the hash. If there already was a match in the hash, that
201
 * data is replaced. This function also "lazily" allocates the table if
202
 * needed, as it is not done in the _init function (anymore).
203
 *
204
 * @unittest: 1305
205
 * @unittest: 1602
206
 * @unittest: 1603
207
 */
208
void *
209
Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p)
210
0
{
211
0
  return Curl_hash_add2(h, key, key_len, p, NULL);
212
0
}
213
214
/* Remove the identified hash entry.
215
 * Returns non-zero on failure.
216
 *
217
 * @unittest: 1603
218
 */
219
int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len)
220
0
{
221
0
  DEBUGASSERT(h);
222
0
  DEBUGASSERT(h->slots);
223
0
  DEBUGASSERT(h->init == HASHINIT);
224
0
  if(h->table) {
225
0
    struct Curl_hash_element *he, **he_anchor;
226
227
0
    he_anchor = CURL_HASH_SLOT_ADDR(h, key, key_len);
228
0
    while(*he_anchor) {
229
0
      he = *he_anchor;
230
0
      if(h->comp_func(he->key, he->key_len, key, key_len)) {
231
0
        hash_elem_unlink(h, he_anchor, he);
232
0
        hash_elem_destroy(h, he);
233
0
        return 0;
234
0
      }
235
0
      he_anchor = &he->next;
236
0
    }
237
0
  }
238
0
  return 1;
239
0
}
240
241
/* Retrieves a hash element.
242
 *
243
 * @unittest: 1603
244
 */
245
void *
246
Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len)
247
0
{
248
0
  DEBUGASSERT(h);
249
0
  DEBUGASSERT(h->init == HASHINIT);
250
0
  if(h->table) {
251
0
    struct Curl_hash_element *he;
252
0
    DEBUGASSERT(h->slots);
253
0
    he = CURL_HASH_SLOT(h, key, key_len);
254
0
    while(he) {
255
0
      if(h->comp_func(he->key, he->key_len, key, key_len)) {
256
0
        return he->ptr;
257
0
      }
258
0
      he = he->next;
259
0
    }
260
0
  }
261
0
  return NULL;
262
0
}
263
264
/* Destroys all the entries in the given hash and resets its attributes,
265
 * prepping the given hash for [static|dynamic] deallocation.
266
 *
267
 * @unittest: 1305
268
 * @unittest: 1602
269
 * @unittest: 1603
270
 */
271
void
272
Curl_hash_destroy(struct Curl_hash *h)
273
6.06k
{
274
6.06k
  DEBUGASSERT(h->init == HASHINIT);
275
6.06k
  if(h->table) {
276
0
    Curl_hash_clean(h);
277
0
    Curl_safefree(h->table);
278
0
  }
279
6.06k
  DEBUGASSERT(h->size == 0);
280
6.06k
  h->slots = 0;
281
6.06k
}
282
283
/* Removes all the entries in the given hash.
284
 *
285
 * @unittest: 1602
286
 */
287
void Curl_hash_clean(struct Curl_hash *h)
288
0
{
289
0
  if(h && h->table) {
290
0
    struct Curl_hash_element *he, **he_anchor;
291
0
    size_t i;
292
0
    DEBUGASSERT(h->init == HASHINIT);
293
0
    for(i = 0; i < h->slots; ++i) {
294
0
      he_anchor = &h->table[i];
295
0
      while(*he_anchor) {
296
0
        he = *he_anchor;
297
0
        hash_elem_unlink(h, he_anchor, he);
298
0
        hash_elem_destroy(h, he);
299
0
      }
300
0
    }
301
0
  }
302
0
}
303
304
size_t Curl_hash_count(struct Curl_hash *h)
305
0
{
306
0
  DEBUGASSERT(h->init == HASHINIT);
307
0
  return h->size;
308
0
}
309
310
/* Cleans all entries that pass the comp function criteria. */
311
void
312
Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user,
313
                               int (*comp)(void *, void *))
314
0
{
315
0
  size_t i;
316
317
0
  if(!h || !h->table)
318
0
    return;
319
320
0
  DEBUGASSERT(h->init == HASHINIT);
321
0
  for(i = 0; i < h->slots; ++i) {
322
0
    struct Curl_hash_element *he, **he_anchor = &h->table[i];
323
0
    while(*he_anchor) {
324
      /* ask the callback function if we shall remove this entry or not */
325
0
      if(!comp || comp(user, (*he_anchor)->ptr)) {
326
0
        he = *he_anchor;
327
0
        hash_elem_unlink(h, he_anchor, he);
328
0
        hash_elem_destroy(h, he);
329
0
      }
330
0
      else
331
0
        he_anchor = &(*he_anchor)->next;
332
0
    }
333
0
  }
334
0
}
335
336
size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num)
337
0
{
338
0
  const char *key_str = (const char *) key;
339
0
  const char *end = key_str + key_length;
340
0
  size_t h = 5381;
341
342
0
  while(key_str < end) {
343
0
    size_t j = (size_t)*key_str++;
344
0
    h += h << 5;
345
0
    h ^= j;
346
0
  }
347
348
0
  return (h % slots_num);
349
0
}
350
351
size_t curlx_str_key_compare(void *k1, size_t key1_len,
352
                             void *k2, size_t key2_len)
353
0
{
354
0
  if((key1_len == key2_len) && !memcmp(k1, k2, key1_len))
355
0
    return 1;
356
357
0
  return 0;
358
0
}
359
360
void Curl_hash_start_iterate(struct Curl_hash *hash,
361
                             struct Curl_hash_iterator *iter)
362
0
{
363
0
  DEBUGASSERT(hash->init == HASHINIT);
364
0
  iter->hash = hash;
365
0
  iter->slot_index = 0;
366
0
  iter->current = NULL;
367
0
#ifdef DEBUGBUILD
368
0
  iter->init = ITERINIT;
369
0
#endif
370
0
}
371
372
struct Curl_hash_element *
373
Curl_hash_next_element(struct Curl_hash_iterator *iter)
374
0
{
375
0
  struct Curl_hash *h;
376
0
  DEBUGASSERT(iter->init == ITERINIT);
377
0
  h = iter->hash;
378
0
  if(!h->table)
379
0
    return NULL; /* empty hash, nothing to return */
380
381
  /* Get the next element in the current list, if any */
382
0
  if(iter->current)
383
0
    iter->current = iter->current->next;
384
385
  /* If we have reached the end of the list, find the next one */
386
0
  if(!iter->current) {
387
0
    size_t i;
388
0
    for(i = iter->slot_index; i < h->slots; i++) {
389
0
      if(h->table[i]) {
390
0
        iter->current = h->table[i];
391
0
        iter->slot_index = i + 1;
392
0
        break;
393
0
      }
394
0
    }
395
0
  }
396
397
0
  return iter->current;
398
0
}