Coverage Report

Created: 2026-10-10 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/strongswan/src/libstrongswan/collections/hashtable.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2008-2020 Tobias Brunner
3
 *
4
 * Copyright (C) secunet Security Networks AG
5
 *
6
 * This program is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU General Public License as published by the
8
 * Free Software Foundation; either version 2 of the License, or (at your
9
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
10
 *
11
 * This program is distributed in the hope that it will be useful, but
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14
 * for more details.
15
 */
16
17
#include "hashtable.h"
18
#include "hashtable_profiler.h"
19
20
#include <utils/chunk.h>
21
#include <utils/debug.h>
22
23
/** The minimum size of the hash table (MUST be a power of 2) */
24
#define MIN_SIZE 8
25
/** The maximum size of the hash table (MUST be a power of 2) */
26
2
#define MAX_SIZE (1 << 30)
27
28
/** Determine the capacity/maximum load of the table (higher values cause
29
 * more collisions, lower values increase the memory overhead) */
30
50.1k
#define CAPACITY(size) (size / 3 * 2)
31
/** Factor for the new table size based on the number of items when resizing,
32
 * with the above load factor this results in doubling the size when growing */
33
2
#define RESIZE_FACTOR 3
34
35
/**
36
 * A note about these parameters:
37
 *
38
 * The maximum number of items that can be stored in this implementation
39
 * is MAX_COUNT = CAPACITY(MAX_SIZE).
40
 * Since we use u_int throughout, MAX_COUNT * RESIZE_FACTOR must not overflow
41
 * this type.
42
 */
43
#if (UINT_MAX / RESIZE_FACTOR < CAPACITY(MAX_SIZE))
44
  #error Hahstable parameters invalid!
45
#endif
46
47
typedef struct pair_t pair_t;
48
49
/**
50
 * This pair holds a pointer to the key and value it represents.
51
 */
52
struct pair_t {
53
54
  /**
55
   * Key of a hash table item.
56
   */
57
  const void *key;
58
59
  /**
60
   * Value of a hash table item.
61
   */
62
  void *value;
63
64
  /**
65
   * Cached hash (used in case of a resize).
66
   */
67
  u_int hash;
68
};
69
70
typedef struct private_hashtable_t private_hashtable_t;
71
72
/**
73
 * Private data of a hashtable_t object.
74
 *
75
 */
76
struct private_hashtable_t {
77
78
  /**
79
   * Public part of hash table.
80
   */
81
  hashtable_t public;
82
83
  /**
84
   * The number of items in the hash table.
85
   */
86
  u_int count;
87
88
  /**
89
   * The current size of the hash table (always a power of 2).
90
   */
91
  u_int size;
92
93
  /**
94
   * The current mask to calculate the row index (size - 1).
95
   */
96
  u_int mask;
97
98
  /**
99
   * All items in the order they were inserted (removed items are marked by
100
   * setting the key to NULL until resized).
101
   */
102
  pair_t *items;
103
104
  /**
105
   * Number of available slots in the array above and the table in general,
106
   * is set to CAPACITY(size) when the hash table is initialized.
107
   */
108
  u_int capacity;
109
110
  /**
111
   * Number of used slots in the array above.
112
   */
113
  u_int items_count;
114
115
  /**
116
   * Hash table with indices into the array above.  The type depends on the
117
   * current capacity.
118
   */
119
  void *table;
120
121
  /**
122
   * The hashing function.
123
   */
124
  hashtable_hash_t hash;
125
126
  /**
127
   * The equality function.
128
   */
129
  hashtable_equals_t equals;
130
131
  /**
132
   * Profiling data
133
   */
134
  hashtable_profile_t profile;
135
};
136
137
typedef struct private_enumerator_t private_enumerator_t;
138
139
/**
140
 * Hash table enumerator implementation
141
 */
142
struct private_enumerator_t {
143
144
  /**
145
   * Implements enumerator interface
146
   */
147
  enumerator_t enumerator;
148
149
  /**
150
   * Associated hash table
151
   */
152
  private_hashtable_t *table;
153
154
  /**
155
   * Current index
156
   */
157
  u_int index;
158
};
159
160
/*
161
 * Described in header
162
 */
163
u_int hashtable_hash_ptr(const void *key)
164
0
{
165
0
  return chunk_hash(chunk_from_thing(key));
166
0
}
167
168
/*
169
 * Described in header
170
 */
171
u_int hashtable_hash_str(const void *key)
172
4.61k
{
173
4.61k
  return chunk_hash(chunk_from_str((char*)key));
174
4.61k
}
175
176
/*
177
 * Described in header
178
 */
179
bool hashtable_equals_ptr(const void *key, const void *other_key)
180
0
{
181
0
  return key == other_key;
182
0
}
183
184
/*
185
 * Described in header
186
 */
187
bool hashtable_equals_str(const void *key, const void *other_key)
188
0
{
189
0
  return streq(key, other_key);
190
0
}
191
192
/**
193
 * Returns the index stored in the given bucket. If the bucket is empty,
194
 * 0 is returned.
195
 */
196
static inline u_int get_index(private_hashtable_t *this, u_int row)
197
4.69k
{
198
4.69k
  if (this->capacity <= 0xff)
199
4.69k
  {
200
4.69k
    return ((uint8_t*)this->table)[row];
201
4.69k
  }
202
0
  else if (this->capacity <= 0xffff)
203
0
  {
204
0
    return ((uint16_t*)this->table)[row];
205
0
  }
206
0
  return ((u_int*)this->table)[row];
207
4.69k
}
208
209
/**
210
 * Set the index stored in the given bucket. Set to 0 to clear a bucket.
211
 */
212
static inline void set_index(private_hashtable_t *this, u_int row, u_int index)
213
4.65k
{
214
4.65k
  if (this->capacity <= 0xff)
215
4.65k
  {
216
4.65k
    ((uint8_t*)this->table)[row] = index;
217
4.65k
  }
218
0
  else if (this->capacity <= 0xffff)
219
0
  {
220
0
    ((uint16_t*)this->table)[row] = index;
221
0
  }
222
0
  else
223
0
  {
224
0
    ((u_int*)this->table)[row] = index;
225
0
  }
226
4.65k
}
227
228
/**
229
 * This function returns the next-highest power of two for the given number.
230
 * The algorithm works by setting all bits on the right-hand side of the most
231
 * significant 1 to 1 and then increments the whole number so it rolls over
232
 * to the nearest power of two. Note: returns 0 for n == 0
233
 *
234
 * Also used by hashlist_t.
235
 */
236
u_int hashtable_get_nearest_powerof2(u_int n)
237
52.4k
{
238
52.4k
  u_int i;
239
240
52.4k
  --n;
241
314k
  for (i = 1; i < sizeof(u_int) * 8; i <<= 1)
242
262k
  {
243
262k
    n |= n >> i;
244
262k
  }
245
52.4k
  return ++n;
246
52.4k
}
247
248
/**
249
 * Init hash table to the given size
250
 */
251
static void init_hashtable(private_hashtable_t *this, u_int size)
252
50.1k
{
253
50.1k
  u_int index_size = sizeof(u_int);
254
255
50.1k
  this->size = max(MIN_SIZE, min(size, MAX_SIZE));
256
50.1k
  this->size = hashtable_get_nearest_powerof2(this->size);
257
50.1k
  this->mask = this->size - 1;
258
50.1k
  profile_size(&this->profile, this->size);
259
260
50.1k
  this->capacity = CAPACITY(this->size);
261
50.1k
  this->items = calloc(this->capacity, sizeof(pair_t));
262
50.1k
  this->items_count = 0;
263
264
50.1k
  if (this->capacity <= 0xff)
265
50.1k
  {
266
50.1k
    index_size = sizeof(uint8_t);
267
50.1k
  }
268
0
  else if (this->capacity <= 0xffff)
269
0
  {
270
0
    index_size = sizeof(uint16_t);
271
0
  }
272
50.1k
  this->table = calloc(this->size, index_size);
273
50.1k
}
274
275
/**
276
 * Calculate the next bucket using quadratic probing (the sequence is h(k) + 1,
277
 * h(k) + 3, h(k) + 6, h(k) + 10, ...).
278
 */
279
static inline u_int get_next(private_hashtable_t *this, u_int row, u_int *p)
280
38
{
281
38
  *p += 1;
282
38
  return (row + *p) & this->mask;
283
38
}
284
285
/**
286
 * Find the pair with the given key, optionally returns the hash and first empty
287
 * or previously used row if the key is not found.
288
 */
289
static inline pair_t *find_key(private_hashtable_t *this, const void *key,
290
                u_int *out_hash, u_int *out_row)
291
4.61k
{
292
4.61k
  pair_t *pair;
293
4.61k
  u_int hash, row, p = 0, removed = 0, index;
294
4.61k
  bool found_removed = FALSE;
295
296
4.61k
  if (!this->count && !out_hash && !out_row)
297
0
  {
298
0
    return NULL;
299
0
  }
300
301
4.61k
  lookup_start();
302
303
4.61k
  hash = this->hash(key);
304
4.61k
  row = hash & this->mask;
305
4.61k
  index = get_index(this, row);
306
4.64k
  while (index)
307
33
  {
308
33
    lookup_probing();
309
33
    pair = &this->items[index-1];
310
311
33
    if (!pair->key)
312
0
    {
313
0
      if (!found_removed && out_row)
314
0
      {
315
0
        removed = row;
316
0
        found_removed = TRUE;
317
0
      }
318
0
    }
319
33
    else if (pair->hash == hash && this->equals(key, pair->key))
320
0
    {
321
0
      lookup_success(&this->profile);
322
0
      return pair;
323
0
    }
324
33
    row = get_next(this, row, &p);
325
33
    index = get_index(this, row);
326
33
  }
327
4.61k
  if (out_hash)
328
4.61k
  {
329
4.61k
    *out_hash = hash;
330
4.61k
  }
331
4.61k
  if (out_row)
332
4.61k
  {
333
4.61k
    *out_row = found_removed ? removed : row;
334
4.61k
  }
335
4.61k
  lookup_failure(&this->profile);
336
4.61k
  return NULL;
337
4.61k
}
338
339
/**
340
 * Helper to insert a new item into the table and items array,
341
 * returns its new index into the latter.
342
 */
343
static inline u_int insert_item(private_hashtable_t *this, u_int row)
344
4.65k
{
345
4.65k
  u_int index = this->items_count++;
346
347
  /* we use 0 to mark unused buckets, so increase the index */
348
4.65k
  set_index(this, row, index + 1);
349
4.65k
  return index;
350
4.65k
}
351
352
/**
353
 * Resize the hash table to the given size and rehash all the elements,
354
 * size may be smaller or even the same (e.g. if it's necessary to clear
355
 * previously used buckets).
356
 */
357
static bool rehash(private_hashtable_t *this, u_int size)
358
2
{
359
2
  pair_t *old_items, *pair;
360
2
  u_int old_count, i, p, row, index;
361
362
2
  if (size > MAX_SIZE)
363
0
  {
364
0
    return FALSE;
365
0
  }
366
367
2
  old_items = this->items;
368
2
  old_count = this->items_count;
369
2
  free(this->table);
370
2
  init_hashtable(this, size);
371
372
  /* no need to do anything if the table is empty and we are just cleaning
373
   * up previously used items */
374
2
  if (this->count)
375
2
  {
376
42
    for (i = 0; i < old_count; i++)
377
40
    {
378
40
      pair = &old_items[i];
379
380
40
      if (pair->key)
381
40
      {
382
40
        row = pair->hash & this->mask;
383
40
        index = get_index(this, row);
384
45
        for (p = 0; index;)
385
5
        {
386
5
          row = get_next(this, row, &p);
387
5
          index = get_index(this, row);
388
5
        }
389
40
        index = insert_item(this, row);
390
40
        this->items[index] = *pair;
391
40
      }
392
40
    }
393
2
  }
394
2
  free(old_items);
395
2
  return TRUE;
396
2
}
397
398
METHOD(hashtable_t, put, void*,
399
  private_hashtable_t *this, const void *key, void *value)
400
4.61k
{
401
4.61k
  void *old_value = NULL;
402
4.61k
  pair_t *pair;
403
4.61k
  u_int index, hash = 0, row = 0;
404
405
4.61k
  if (this->items_count >= this->capacity &&
406
2
    !rehash(this, this->count * RESIZE_FACTOR))
407
0
  {
408
0
    DBG1(DBG_LIB, "!!! FAILED TO RESIZE HASHTABLE TO %u !!!",
409
0
       this->count * RESIZE_FACTOR);
410
0
    return NULL;
411
0
  }
412
4.61k
  pair = find_key(this, key, &hash, &row);
413
4.61k
  if (pair)
414
0
  {
415
0
    old_value = pair->value;
416
0
    pair->value = value;
417
0
    pair->key = key;
418
0
    return old_value;
419
0
  }
420
4.61k
  index = insert_item(this, row);
421
4.61k
  this->items[index] = (pair_t){
422
4.61k
    .hash = hash,
423
4.61k
    .key = key,
424
4.61k
    .value = value,
425
4.61k
  };
426
4.61k
  this->count++;
427
4.61k
  profile_count(&this->profile, this->count);
428
4.61k
  return NULL;
429
4.61k
}
430
431
METHOD(hashtable_t, get, void*,
432
  private_hashtable_t *this, const void *key)
433
0
{
434
0
  pair_t *pair = find_key(this, key, NULL, NULL);
435
0
  return pair ? pair->value : NULL;
436
0
}
437
438
/**
439
 * Remove the given item from the table, returns the currently stored value.
440
 */
441
static void *remove_internal(private_hashtable_t *this, pair_t *pair)
442
0
{
443
0
  void *value = NULL;
444
445
0
  if (pair)
446
0
  { /* this does not decrease the item count as we keep the previously
447
     * used items until the table is rehashed/resized */
448
0
    value = pair->value;
449
0
    pair->key = NULL;
450
0
    this->count--;
451
0
  }
452
0
  return value;
453
0
}
454
455
METHOD(hashtable_t, remove_, void*,
456
  private_hashtable_t *this, const void *key)
457
0
{
458
0
  pair_t *pair = find_key(this, key, NULL, NULL);
459
0
  return remove_internal(this, pair);
460
0
}
461
462
METHOD(hashtable_t, remove_at, void,
463
  private_hashtable_t *this, private_enumerator_t *enumerator)
464
0
{
465
0
  if (enumerator->table == this && enumerator->index)
466
0
  { /* the index is already advanced by one */
467
0
    u_int index = enumerator->index - 1;
468
0
    remove_internal(this, &this->items[index]);
469
0
  }
470
0
}
471
472
METHOD(hashtable_t, get_count, u_int,
473
  private_hashtable_t *this)
474
0
{
475
0
  return this->count;
476
0
}
477
478
METHOD(enumerator_t, enumerate, bool,
479
  private_enumerator_t *this, va_list args)
480
2.27k
{
481
2.27k
  const void **key;
482
2.27k
  void **value;
483
2.27k
  pair_t *pair;
484
485
2.27k
  VA_ARGS_VGET(args, key, value);
486
487
2.27k
  while (this->index < this->table->items_count)
488
0
  {
489
0
    pair = &this->table->items[this->index++];
490
0
    if (pair->key)
491
0
    {
492
0
      if (key)
493
0
      {
494
0
        *key = pair->key;
495
0
      }
496
0
      if (value)
497
0
      {
498
0
        *value = pair->value;
499
0
      }
500
0
      return TRUE;
501
0
    }
502
0
  }
503
2.27k
  return FALSE;
504
2.27k
}
505
506
METHOD(hashtable_t, create_enumerator, enumerator_t*,
507
  private_hashtable_t *this)
508
2.27k
{
509
2.27k
  private_enumerator_t *enumerator;
510
511
2.27k
  INIT(enumerator,
512
2.27k
    .enumerator = {
513
2.27k
      .enumerate = enumerator_enumerate_default,
514
2.27k
      .venumerate = _enumerate,
515
2.27k
      .destroy = (void*)free,
516
2.27k
    },
517
2.27k
    .table = this,
518
2.27k
  );
519
2.27k
  return &enumerator->enumerator;
520
2.27k
}
521
522
static void destroy_internal(private_hashtable_t *this,
523
               void (*fn)(void*,const void*))
524
50.1k
{
525
50.1k
  pair_t *pair;
526
50.1k
  u_int i;
527
528
50.1k
  profiler_cleanup(&this->profile, this->count, this->size);
529
530
50.1k
  if (fn)
531
4.55k
  {
532
9.11k
    for (i = 0; i < this->items_count; i++)
533
4.55k
    {
534
4.55k
      pair = &this->items[i];
535
4.55k
      if (pair->key)
536
4.55k
      {
537
4.55k
        fn(pair->value, pair->key);
538
4.55k
      }
539
4.55k
    }
540
4.55k
  }
541
50.1k
  free(this->items);
542
50.1k
  free(this->table);
543
50.1k
  free(this);
544
50.1k
}
545
546
METHOD(hashtable_t, destroy, void,
547
  private_hashtable_t *this)
548
45.5k
{
549
45.5k
  destroy_internal(this, NULL);
550
45.5k
}
551
552
METHOD(hashtable_t, destroy_function, void,
553
  private_hashtable_t *this, void (*fn)(void*,const void*))
554
4.55k
{
555
4.55k
  destroy_internal(this, fn);
556
4.55k
}
557
558
/*
559
 * Described in header.
560
 */
561
hashtable_t *hashtable_create(hashtable_hash_t hash, hashtable_equals_t equals,
562
                u_int size)
563
50.1k
{
564
50.1k
  private_hashtable_t *this;
565
566
50.1k
  INIT(this,
567
50.1k
    .public = {
568
50.1k
      .put = _put,
569
50.1k
      .get = _get,
570
50.1k
      .remove = _remove_,
571
50.1k
      .remove_at = (void*)_remove_at,
572
50.1k
      .get_count = _get_count,
573
50.1k
      .create_enumerator = _create_enumerator,
574
50.1k
      .destroy = _destroy,
575
50.1k
      .destroy_function = _destroy_function,
576
50.1k
    },
577
50.1k
    .hash = hash,
578
50.1k
    .equals = equals,
579
50.1k
  );
580
581
50.1k
  init_hashtable(this, size);
582
583
50.1k
  profiler_init(&this->profile, 2);
584
585
50.1k
  return &this->public;
586
50.1k
}