Coverage Report

Created: 2026-09-04 07:15

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