Coverage Report

Created: 2026-06-10 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/p11-kit/common/dict.c
Line
Count
Source
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
2.84M
{
68
2.84M
  dictbucket *bucket = iter->next;
69
6.33M
  while (!bucket) {
70
3.50M
    if (iter->index >= iter->dict->num_buckets)
71
18.2k
      return NULL;
72
3.48M
    bucket = iter->dict->buckets[iter->index++];
73
3.48M
  }
74
2.82M
  iter->next = bucket->next;
75
2.82M
  return bucket;
76
2.84M
}
77
78
79
bool
80
p11_dict_next (p11_dictiter *iter,
81
               void **key,
82
               void **value)
83
8.99k
{
84
8.99k
  dictbucket *bucket = next_entry (iter);
85
8.99k
  if (bucket == NULL)
86
2.84k
    return false;
87
6.14k
  if (key)
88
2.99k
    *key = bucket->key;
89
6.14k
  if (value)
90
3.15k
    *value = bucket->value;
91
6.14k
  return true;
92
8.99k
}
93
94
void
95
p11_dict_iterate (p11_dict *dict,
96
                  p11_dictiter *iter)
97
18.2k
{
98
18.2k
  iter->dict = dict;
99
18.2k
  iter->index = 0;
100
18.2k
  iter->next = NULL;
101
18.2k
}
102
103
static dictbucket **
104
lookup_or_create_bucket (p11_dict *dict,
105
                         const void *key,
106
                         bool create)
107
1.14M
{
108
1.14M
  dictbucket **bucketp;
109
1.14M
  unsigned int hash;
110
111
  /* Perform the hashing */
112
1.14M
  hash = dict->hash_func (key);
113
114
  /* scan linked list */
115
1.14M
  for (bucketp = &dict->buckets[hash % dict->num_buckets];
116
1.94M
       *bucketp != NULL; bucketp = &(*bucketp)->next) {
117
821k
    if((*bucketp)->hashed == hash && dict->equal_func ((*bucketp)->key, key))
118
17.7k
      break;
119
821k
  }
120
121
1.14M
  if ((*bucketp) != NULL || !create)
122
32.1k
    return bucketp;
123
124
  /* add a new entry for non-NULL val */
125
1.10M
  (*bucketp) = calloc (1, sizeof (dictbucket));
126
127
1.10M
  if (*bucketp != NULL) {
128
1.10M
    (*bucketp)->key = (void*)key;
129
1.10M
    (*bucketp)->hashed = hash;
130
1.10M
    dict->num_items++;
131
1.10M
  }
132
133
1.10M
  return bucketp;
134
1.14M
}
135
136
void *
137
p11_dict_get (p11_dict *dict,
138
              const void *key)
139
26.4k
{
140
26.4k
  dictbucket **bucketp;
141
142
26.4k
  bucketp = lookup_or_create_bucket (dict, key, false);
143
26.4k
  if (bucketp && *bucketp)
144
15.0k
    return (void*)((*bucketp)->value);
145
11.3k
  else
146
11.3k
    return NULL;
147
26.4k
}
148
149
bool
150
p11_dict_set (p11_dict *dict,
151
              void *key,
152
              void *val)
153
1.11M
{
154
1.11M
  dictbucket **bucketp;
155
1.11M
  p11_dictiter iter;
156
1.11M
  dictbucket *bucket;
157
1.11M
  dictbucket **new_buckets;
158
1.11M
  unsigned int num_buckets;
159
160
1.11M
  bucketp = lookup_or_create_bucket (dict, key, true);
161
1.11M
  if(bucketp && *bucketp) {
162
163
    /* Destroy the previous key */
164
1.11M
    if ((*bucketp)->key && (*bucketp)->key != key && dict->key_destroy_func)
165
10
      dict->key_destroy_func ((*bucketp)->key);
166
167
    /* Destroy the previous value */
168
1.11M
    if ((*bucketp)->value && (*bucketp)->value != val && dict->value_destroy_func)
169
0
      dict->value_destroy_func ((*bucketp)->value);
170
171
    /* replace entry */
172
1.11M
    (*bucketp)->key = key;
173
1.11M
    (*bucketp)->value = val;
174
175
    /* check that the collision rate isn't too high */
176
1.11M
    if (dict->num_items > dict->num_buckets) {
177
9.42k
      num_buckets = dict->num_buckets * 2 + 1;
178
9.42k
      new_buckets = (dictbucket **)calloc (num_buckets, sizeof (dictbucket *));
179
180
      /* Ignore failures, maybe we can expand later */
181
9.42k
      if(new_buckets) {
182
9.42k
        p11_dict_iterate (dict, &iter);
183
1.71M
        while ((bucket = next_entry (&iter)) != NULL) {
184
1.70M
          unsigned int i = bucket->hashed % num_buckets;
185
1.70M
          bucket->next = new_buckets[i];
186
1.70M
          new_buckets[i] = bucket;
187
1.70M
        }
188
189
9.42k
        free (dict->buckets);
190
9.42k
        dict->buckets = new_buckets;
191
9.42k
        dict->num_buckets = num_buckets;
192
9.42k
      }
193
9.42k
    }
194
195
1.11M
    return true;
196
1.11M
  }
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
2.99k
{
207
2.99k
  dictbucket **bucketp;
208
209
2.99k
  bucketp = lookup_or_create_bucket (dict, key, false);
210
2.99k
  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
2.99k
  return false;
223
224
2.99k
}
225
226
bool
227
p11_dict_remove (p11_dict *dict,
228
                 const void *key)
229
2.99k
{
230
2.99k
  void *old_key;
231
2.99k
  void *old_value;
232
233
2.99k
  if (!p11_dict_steal (dict, key, &old_key, &old_value))
234
2.99k
    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
2.99k
}
242
243
void
244
p11_dict_clear (p11_dict *dict)
245
172
{
246
172
  dictbucket *bucket, *next;
247
172
  unsigned int i;
248
249
  /* Free all entries in the array */
250
1.72k
  for (i = 0; i < dict->num_buckets; ++i) {
251
1.54k
    bucket = dict->buckets[i];
252
1.97k
    while (bucket != NULL) {
253
425
      next = bucket->next;
254
425
      if (dict->key_destroy_func)
255
0
        dict->key_destroy_func (bucket->key);
256
425
      if (dict->value_destroy_func)
257
425
        dict->value_destroy_func (bucket->value);
258
425
      free (bucket);
259
425
      bucket = next;
260
425
    }
261
1.54k
  }
262
263
172
  memset (dict->buckets, 0, dict->num_buckets * sizeof (dictbucket *));
264
172
  dict->num_items = 0;
265
172
}
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
6.01k
{
273
6.01k
  p11_dict *dict;
274
275
6.01k
  assert (hash_func);
276
6.01k
  assert (equal_func);
277
278
6.01k
  dict = malloc (sizeof (p11_dict));
279
6.01k
  if (dict) {
280
6.01k
    dict->hash_func = hash_func;
281
6.01k
    dict->equal_func = equal_func;
282
6.01k
    dict->key_destroy_func = key_destroy_func;
283
6.01k
    dict->value_destroy_func = value_destroy_func;
284
285
6.01k
    dict->num_buckets = 9;
286
6.01k
    dict->buckets = (dictbucket **)calloc (dict->num_buckets, sizeof (dictbucket *));
287
6.01k
    if (!dict->buckets) {
288
0
      free (dict);
289
0
      return NULL;
290
0
    }
291
292
6.01k
    dict->num_items = 0;
293
6.01k
  }
294
295
6.01k
  return dict;
296
6.01k
}
297
298
void
299
p11_dict_free (p11_dict *dict)
300
7.17k
{
301
7.17k
  dictbucket *bucket;
302
7.17k
  p11_dictiter iter;
303
304
7.17k
  if (!dict)
305
1.16k
    return;
306
307
6.00k
  p11_dict_iterate (dict, &iter);
308
1.11M
  while ((bucket = next_entry (&iter)) != NULL) {
309
1.10M
    if (dict->key_destroy_func)
310
3.15k
      dict->key_destroy_func (bucket->key);
311
1.10M
    if (dict->value_destroy_func)
312
4.72k
      dict->value_destroy_func (bucket->value);
313
1.10M
    free (bucket);
314
1.10M
  }
315
316
6.00k
  if (dict->buckets)
317
6.00k
    free (dict->buckets);
318
319
6.00k
  free (dict);
320
6.00k
}
321
322
unsigned int
323
p11_dict_size (p11_dict *dict)
324
2.49k
{
325
2.49k
  return dict->num_items;
326
2.49k
}
327
328
unsigned int
329
p11_dict_str_hash (const void *string)
330
1.14M
{
331
1.14M
  uint32_t hash;
332
1.14M
  p11_hash_murmur3 (&hash, string, strlen (string), NULL);
333
1.14M
  return hash;
334
1.14M
}
335
336
bool
337
p11_dict_str_equal (const void *string_one,
338
                    const void *string_two)
339
17.7k
{
340
17.7k
  assert (string_one);
341
17.7k
  assert (string_two);
342
343
17.7k
  return strcmp (string_one, string_two) == 0;
344
17.7k
}
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
860
{
381
860
  return (unsigned int)(size_t)ptr;
382
860
}
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
}