Coverage Report

Created: 2024-02-29 06:05

/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
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
73.8k
#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
60
{
165
60
  return chunk_hash(chunk_from_thing(key));
166
60
}
167
168
/*
169
 * Described in header
170
 */
171
u_int hashtable_hash_str(const void *key)
172
28.1k
{
173
28.1k
  return chunk_hash(chunk_from_str((char*)key));
174
28.1k
}
175
176
/*
177
 * Described in header
178
 */
179
bool hashtable_equals_ptr(const void *key, const void *other_key)
180
30
{
181
30
  return key == other_key;
182
30
}
183
184
/*
185
 * Described in header
186
 */
187
bool hashtable_equals_str(const void *key, const void *other_key)
188
21.0k
{
189
21.0k
  return streq(key, other_key);
190
21.0k
}
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
28.3k
{
198
28.3k
  if (this->capacity <= 0xff)
199
28.3k
  {
200
28.3k
    return ((uint8_t*)this->table)[row];
201
28.3k
  }
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
28.3k
}
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
7.16k
{
214
7.16k
  if (this->capacity <= 0xff)
215
7.16k
  {
216
7.16k
    ((uint8_t*)this->table)[row] = index;
217
7.16k
  }
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
7.16k
}
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
80.8k
{
238
80.8k
  u_int i;
239
240
80.8k
  --n;
241
485k
  for (i = 1; i < sizeof(u_int) * 8; i <<= 1)
242
404k
  {
243
404k
    n |= n >> i;
244
404k
  }
245
80.8k
  return ++n;
246
80.8k
}
247
248
/**
249
 * Init hash table to the given size
250
 */
251
static void init_hashtable(private_hashtable_t *this, u_int size)
252
73.8k
{
253
73.8k
  u_int index_size = sizeof(u_int);
254
255
73.8k
  this->size = max(MIN_SIZE, min(size, MAX_SIZE));
256
73.8k
  this->size = hashtable_get_nearest_powerof2(this->size);
257
73.8k
  this->mask = this->size - 1;
258
73.8k
  profile_size(&this->profile, this->size);
259
260
73.8k
  this->capacity = CAPACITY(this->size);
261
73.8k
  this->items = calloc(this->capacity, sizeof(pair_t));
262
73.8k
  this->items_count = 0;
263
264
73.8k
  if (this->capacity <= 0xff)
265
73.8k
  {
266
73.8k
    index_size = sizeof(uint8_t);
267
73.8k
  }
268
0
  else if (this->capacity <= 0xffff)
269
0
  {
270
0
    index_size = sizeof(uint16_t);
271
0
  }
272
73.8k
  this->table = calloc(this->size, index_size);
273
73.8k
}
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
63
{
281
63
  *p += 1;
282
63
  return (row + *p) & this->mask;
283
63
}
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
40.3k
{
292
40.3k
  pair_t *pair;
293
40.3k
  u_int hash, row, p = 0, removed = 0, index;
294
40.3k
  bool found_removed = FALSE;
295
296
40.3k
  if (!this->count && !out_hash && !out_row)
297
12.1k
  {
298
12.1k
    return NULL;
299
12.1k
  }
300
301
28.2k
  lookup_start();
302
303
28.2k
  hash = this->hash(key);
304
28.2k
  row = hash & this->mask;
305
28.2k
  index = get_index(this, row);
306
28.2k
  while (index)
307
21.1k
  {
308
21.1k
    lookup_probing();
309
21.1k
    pair = &this->items[index-1];
310
311
21.1k
    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
21.1k
    else if (pair->hash == hash && this->equals(key, pair->key))
320
21.1k
    {
321
21.1k
      lookup_success(&this->profile);
322
21.1k
      return pair;
323
21.1k
    }
324
45
    row = get_next(this, row, &p);
325
45
    index = get_index(this, row);
326
45
  }
327
7.12k
  if (out_hash)
328
7.12k
  {
329
7.12k
    *out_hash = hash;
330
7.12k
  }
331
7.12k
  if (out_row)
332
7.12k
  {
333
7.12k
    *out_row = found_removed ? removed : row;
334
7.12k
  }
335
7.12k
  lookup_failure(&this->profile);
336
7.12k
  return NULL;
337
28.2k
}
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
7.16k
{
345
7.16k
  u_int index = this->items_count++;
346
347
  /* we use 0 to mark unused buckets, so increase the index */
348
7.16k
  set_index(this, row, index + 1);
349
7.16k
  return index;
350
7.16k
}
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
58
        for (p = 0; index;)
385
18
        {
386
18
          row = get_next(this, row, &p);
387
18
          index = get_index(this, row);
388
18
        }
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
7.12k
{
401
7.12k
  void *old_value = NULL;
402
7.12k
  pair_t *pair;
403
7.12k
  u_int index, hash = 0, row = 0;
404
405
7.12k
  if (this->items_count >= this->capacity &&
406
7.12k
    !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
7.12k
  pair = find_key(this, key, &hash, &row);
413
7.12k
  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
7.12k
  index = insert_item(this, row);
421
7.12k
  this->items[index] = (pair_t){
422
7.12k
    .hash = hash,
423
7.12k
    .key = key,
424
7.12k
    .value = value,
425
7.12k
  };
426
7.12k
  this->count++;
427
7.12k
  profile_count(&this->profile, this->count);
428
7.12k
  return NULL;
429
7.12k
}
430
431
METHOD(hashtable_t, get, void*,
432
  private_hashtable_t *this, const void *key)
433
21.1k
{
434
21.1k
  pair_t *pair = find_key(this, key, NULL, NULL);
435
21.1k
  return pair ? pair->value : NULL;
436
21.1k
}
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
12.0k
{
443
12.0k
  void *value = NULL;
444
445
12.0k
  if (pair)
446
30
  { /* this does not decrease the item count as we keep the previously
447
     * used items until the table is rehashed/resized */
448
30
    value = pair->value;
449
30
    pair->key = NULL;
450
30
    this->count--;
451
30
  }
452
12.0k
  return value;
453
12.0k
}
454
455
METHOD(hashtable_t, remove_, void*,
456
  private_hashtable_t *this, const void *key)
457
12.0k
{
458
12.0k
  pair_t *pair = find_key(this, key, NULL, NULL);
459
12.0k
  return remove_internal(this, pair);
460
12.0k
}
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
3.51k
{
481
3.51k
  const void **key;
482
3.51k
  void **value;
483
3.51k
  pair_t *pair;
484
485
3.51k
  VA_ARGS_VGET(args, key, value);
486
487
3.51k
  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
3.51k
  return FALSE;
504
3.51k
}
505
506
METHOD(hashtable_t, create_enumerator, enumerator_t*,
507
  private_hashtable_t *this)
508
3.51k
{
509
3.51k
  private_enumerator_t *enumerator;
510
511
3.51k
  INIT(enumerator,
512
3.51k
    .enumerator = {
513
3.51k
      .enumerate = enumerator_enumerate_default,
514
3.51k
      .venumerate = _enumerate,
515
3.51k
      .destroy = (void*)free,
516
3.51k
    },
517
3.51k
    .table = this,
518
3.51k
  );
519
3.51k
  return &enumerator->enumerator;
520
3.51k
}
521
522
static void destroy_internal(private_hashtable_t *this,
523
               void (*fn)(void*,const void*))
524
73.8k
{
525
73.8k
  pair_t *pair;
526
73.8k
  u_int i;
527
528
73.8k
  profiler_cleanup(&this->profile, this->count, this->size);
529
530
73.8k
  if (fn)
531
3.51k
  {
532
10.5k
    for (i = 0; i < this->items_count; i++)
533
7.03k
    {
534
7.03k
      pair = &this->items[i];
535
7.03k
      if (pair->key)
536
7.03k
      {
537
7.03k
        fn(pair->value, pair->key);
538
7.03k
      }
539
7.03k
    }
540
3.51k
  }
541
73.8k
  free(this->items);
542
73.8k
  free(this->table);
543
73.8k
  free(this);
544
73.8k
}
545
546
METHOD(hashtable_t, destroy, void,
547
  private_hashtable_t *this)
548
70.3k
{
549
70.3k
  destroy_internal(this, NULL);
550
70.3k
}
551
552
METHOD(hashtable_t, destroy_function, void,
553
  private_hashtable_t *this, void (*fn)(void*,const void*))
554
3.51k
{
555
3.51k
  destroy_internal(this, fn);
556
3.51k
}
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
73.8k
{
564
73.8k
  private_hashtable_t *this;
565
566
73.8k
  INIT(this,
567
73.8k
    .public = {
568
73.8k
      .put = _put,
569
73.8k
      .get = _get,
570
73.8k
      .remove = _remove_,
571
73.8k
      .remove_at = (void*)_remove_at,
572
73.8k
      .get_count = _get_count,
573
73.8k
      .create_enumerator = _create_enumerator,
574
73.8k
      .destroy = _destroy,
575
73.8k
      .destroy_function = _destroy_function,
576
73.8k
    },
577
73.8k
    .hash = hash,
578
73.8k
    .equals = equals,
579
73.8k
  );
580
581
73.8k
  init_hashtable(this, size);
582
583
73.8k
  profiler_init(&this->profile, 2);
584
585
73.8k
  return &this->public;
586
73.8k
}