Coverage Report

Created: 2025-07-23 07:07

/src/strongswan/src/libstrongswan/collections/hashtable.c
Line
Count
Source (jump to first uncovered line)
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
0
#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
24
#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
0
#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
41
{
173
41
  return chunk_hash(chunk_from_str((char*)key));
174
41
}
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
3
{
189
3
  return streq(key, other_key);
190
3
}
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
55
{
198
55
  if (this->capacity <= 0xff)
199
55
  {
200
55
    return ((uint8_t*)this->table)[row];
201
55
  }
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
55
}
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
38
{
214
38
  if (this->capacity <= 0xff)
215
38
  {
216
38
    ((uint8_t*)this->table)[row] = index;
217
38
  }
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
38
}
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
25
{
238
25
  u_int i;
239
240
25
  --n;
241
150
  for (i = 1; i < sizeof(u_int) * 8; i <<= 1)
242
125
  {
243
125
    n |= n >> i;
244
125
  }
245
25
  return ++n;
246
25
}
247
248
/**
249
 * Init hash table to the given size
250
 */
251
static void init_hashtable(private_hashtable_t *this, u_int size)
252
24
{
253
24
  u_int index_size = sizeof(u_int);
254
255
24
  this->size = max(MIN_SIZE, min(size, MAX_SIZE));
256
24
  this->size = hashtable_get_nearest_powerof2(this->size);
257
24
  this->mask = this->size - 1;
258
24
  profile_size(&this->profile, this->size);
259
260
24
  this->capacity = CAPACITY(this->size);
261
24
  this->items = calloc(this->capacity, sizeof(pair_t));
262
24
  this->items_count = 0;
263
264
24
  if (this->capacity <= 0xff)
265
24
  {
266
24
    index_size = sizeof(uint8_t);
267
24
  }
268
0
  else if (this->capacity <= 0xffff)
269
0
  {
270
0
    index_size = sizeof(uint16_t);
271
0
  }
272
24
  this->table = calloc(this->size, index_size);
273
24
}
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
14
{
281
14
  *p += 1;
282
14
  return (row + *p) & this->mask;
283
14
}
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
41
{
292
41
  pair_t *pair;
293
41
  u_int hash, row, p = 0, removed = 0, index;
294
41
  bool found_removed = FALSE;
295
296
41
  if (!this->count && !out_hash && !out_row)
297
0
  {
298
0
    return NULL;
299
0
  }
300
301
41
  lookup_start();
302
303
41
  hash = this->hash(key);
304
41
  row = hash & this->mask;
305
41
  index = get_index(this, row);
306
55
  while (index)
307
17
  {
308
17
    lookup_probing();
309
17
    pair = &this->items[index-1];
310
311
17
    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
17
    else if (pair->hash == hash && this->equals(key, pair->key))
320
3
    {
321
3
      lookup_success(&this->profile);
322
3
      return pair;
323
3
    }
324
14
    row = get_next(this, row, &p);
325
14
    index = get_index(this, row);
326
14
  }
327
38
  if (out_hash)
328
38
  {
329
38
    *out_hash = hash;
330
38
  }
331
38
  if (out_row)
332
38
  {
333
38
    *out_row = found_removed ? removed : row;
334
38
  }
335
38
  lookup_failure(&this->profile);
336
38
  return NULL;
337
41
}
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
38
{
345
38
  u_int index = this->items_count++;
346
347
  /* we use 0 to mark unused buckets, so increase the index */
348
38
  set_index(this, row, index + 1);
349
38
  return index;
350
38
}
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
0
{
359
0
  pair_t *old_items, *pair;
360
0
  u_int old_count, i, p, row, index;
361
362
0
  if (size > MAX_SIZE)
363
0
  {
364
0
    return FALSE;
365
0
  }
366
367
0
  old_items = this->items;
368
0
  old_count = this->items_count;
369
0
  free(this->table);
370
0
  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
0
  if (this->count)
375
0
  {
376
0
    for (i = 0; i < old_count; i++)
377
0
    {
378
0
      pair = &old_items[i];
379
380
0
      if (pair->key)
381
0
      {
382
0
        row = pair->hash & this->mask;
383
0
        index = get_index(this, row);
384
0
        for (p = 0; index;)
385
0
        {
386
0
          row = get_next(this, row, &p);
387
0
          index = get_index(this, row);
388
0
        }
389
0
        index = insert_item(this, row);
390
0
        this->items[index] = *pair;
391
0
      }
392
0
    }
393
0
  }
394
0
  free(old_items);
395
0
  return TRUE;
396
0
}
397
398
METHOD(hashtable_t, put, void*,
399
  private_hashtable_t *this, const void *key, void *value)
400
38
{
401
38
  void *old_value = NULL;
402
38
  pair_t *pair;
403
38
  u_int index, hash = 0, row = 0;
404
405
38
  if (this->items_count >= this->capacity &&
406
38
    !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
38
  pair = find_key(this, key, &hash, &row);
413
38
  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
38
  index = insert_item(this, row);
421
38
  this->items[index] = (pair_t){
422
38
    .hash = hash,
423
38
    .key = key,
424
38
    .value = value,
425
38
  };
426
38
  this->count++;
427
38
  profile_count(&this->profile, this->count);
428
38
  return NULL;
429
38
}
430
431
METHOD(hashtable_t, get, void*,
432
  private_hashtable_t *this, const void *key)
433
3
{
434
3
  pair_t *pair = find_key(this, key, NULL, NULL);
435
3
  return pair ? pair->value : NULL;
436
3
}
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
1
{
481
1
  const void **key;
482
1
  void **value;
483
1
  pair_t *pair;
484
485
1
  VA_ARGS_VGET(args, key, value);
486
487
1
  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
1
  return FALSE;
504
1
}
505
506
METHOD(hashtable_t, create_enumerator, enumerator_t*,
507
  private_hashtable_t *this)
508
1
{
509
1
  private_enumerator_t *enumerator;
510
511
1
  INIT(enumerator,
512
1
    .enumerator = {
513
1
      .enumerate = enumerator_enumerate_default,
514
1
      .venumerate = _enumerate,
515
1
      .destroy = (void*)free,
516
1
    },
517
1
    .table = this,
518
1
  );
519
1
  return &enumerator->enumerator;
520
1
}
521
522
static void destroy_internal(private_hashtable_t *this,
523
               void (*fn)(void*,const void*))
524
22
{
525
22
  pair_t *pair;
526
22
  u_int i;
527
528
22
  profiler_cleanup(&this->profile, this->count, this->size);
529
530
22
  if (fn)
531
2
  {
532
4
    for (i = 0; i < this->items_count; i++)
533
2
    {
534
2
      pair = &this->items[i];
535
2
      if (pair->key)
536
2
      {
537
2
        fn(pair->value, pair->key);
538
2
      }
539
2
    }
540
2
  }
541
22
  free(this->items);
542
22
  free(this->table);
543
22
  free(this);
544
22
}
545
546
METHOD(hashtable_t, destroy, void,
547
  private_hashtable_t *this)
548
20
{
549
20
  destroy_internal(this, NULL);
550
20
}
551
552
METHOD(hashtable_t, destroy_function, void,
553
  private_hashtable_t *this, void (*fn)(void*,const void*))
554
2
{
555
2
  destroy_internal(this, fn);
556
2
}
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
24
{
564
24
  private_hashtable_t *this;
565
566
24
  INIT(this,
567
24
    .public = {
568
24
      .put = _put,
569
24
      .get = _get,
570
24
      .remove = _remove_,
571
24
      .remove_at = (void*)_remove_at,
572
24
      .get_count = _get_count,
573
24
      .create_enumerator = _create_enumerator,
574
24
      .destroy = _destroy,
575
24
      .destroy_function = _destroy_function,
576
24
    },
577
24
    .hash = hash,
578
24
    .equals = equals,
579
24
  );
580
581
24
  init_hashtable(this, size);
582
583
24
  profiler_init(&this->profile, 2);
584
585
24
  return &this->public;
586
24
}