Coverage Report

Created: 2025-12-04 06:52

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