Coverage Report

Created: 2024-02-29 06:05

/src/strongswan/src/libstrongswan/collections/hashlist.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
#ifdef HASHTABLE_PROFILER
23
#include <utils/backtrace.h>
24
#endif
25
26
/** The minimum size of the hash table (MUST be a power of 2) */
27
#define MIN_SIZE 8
28
/** The maximum size of the hash table (MUST be a power of 2) */
29
3.92k
#define MAX_SIZE (1 << 30)
30
31
/** Maximum load factor before the hash table is resized */
32
297k
#define LOAD_FACTOR 0.75f
33
34
/** Provided by hashtable_t */
35
u_int hashtable_get_nearest_powerof2(u_int n);
36
37
typedef struct pair_t pair_t;
38
39
/**
40
 * This pair holds a pointer to the key and value it represents.
41
 */
42
struct pair_t {
43
44
  /**
45
   * Key of a hash table item.
46
   */
47
  const void *key;
48
49
  /**
50
   * Value of a hash table item.
51
   */
52
  void *value;
53
54
  /**
55
   * Cached hash (used in case of a resize).
56
   */
57
  u_int hash;
58
59
  /**
60
   * Next pair in an overflow list.
61
   */
62
  pair_t *next;
63
};
64
65
typedef struct private_hashlist_t private_hashlist_t;
66
67
/**
68
 * Private data of a hashlist_t object.
69
 */
70
struct private_hashlist_t {
71
72
  /**
73
   * Public interface.
74
   */
75
  hashlist_t public;
76
77
  /**
78
   * The number of items in the hash table.
79
   */
80
  u_int count;
81
82
  /**
83
   * The current size of the hash table (always a power of 2).
84
   */
85
  u_int size;
86
87
  /**
88
   * The current mask to calculate the row index (size - 1).
89
   */
90
  u_int mask;
91
92
  /**
93
   * The actual table.
94
   */
95
  pair_t **table;
96
97
  /**
98
   * The hashing function.
99
   */
100
  hashtable_hash_t hash;
101
102
  /**
103
   * The equality function.
104
   */
105
  hashtable_equals_t equals;
106
107
  /**
108
   * Alternative comparison function.
109
   */
110
  hashtable_cmp_t cmp;
111
112
  /**
113
   * Profiling information
114
   */
115
  hashtable_profile_t profile;
116
};
117
118
typedef struct private_enumerator_t private_enumerator_t;
119
120
/**
121
 * Hash table enumerator implementation
122
 */
123
struct private_enumerator_t {
124
125
  /**
126
   * Implements enumerator interface.
127
   */
128
  enumerator_t enumerator;
129
130
  /**
131
   * Associated hash table.
132
   */
133
  private_hashlist_t *table;
134
135
  /**
136
   * Current row index.
137
   */
138
  u_int row;
139
140
  /**
141
   * Number of remaining items to enumerate.
142
   */
143
  u_int count;
144
145
  /**
146
   * Current pair.
147
   */
148
  pair_t *current;
149
150
  /**
151
   * Previous pair (used by remove_at).
152
   */
153
  pair_t *prev;
154
};
155
156
/**
157
 * Init hash table parameters
158
 */
159
static void init_hashtable(private_hashlist_t *this, u_int size)
160
7.84k
{
161
7.84k
  size = max(MIN_SIZE, min(size, MAX_SIZE));
162
7.84k
  this->size = hashtable_get_nearest_powerof2(size);
163
7.84k
  this->mask = this->size - 1;
164
7.84k
  profile_size(&this->profile, this->size);
165
166
7.84k
  this->table = calloc(this->size, sizeof(pair_t*));
167
7.84k
}
168
169
/**
170
 * Insert an item into a bucket.
171
 */
172
static inline void insert_pair(private_hashlist_t *this, pair_t *to_insert,
173
                 pair_t *prev)
174
486k
{
175
486k
  u_int row;
176
177
486k
  if (prev)
178
164k
  {
179
164k
    to_insert->next = prev->next;
180
164k
    prev->next = to_insert;
181
164k
  }
182
321k
  else
183
321k
  {
184
321k
    row = to_insert->hash & this->mask;
185
321k
    to_insert->next = this->table[row];
186
321k
    this->table[row] = to_insert;
187
321k
  }
188
486k
}
189
190
/**
191
 * Double the size of the hash table and rehash all the elements.
192
 */
193
static void rehash(private_hashlist_t *this)
194
3.92k
{
195
3.92k
  pair_t **old_table, *to_move, *pair, *next;
196
3.92k
  u_int row, old_size;
197
198
3.92k
  if (this->size >= MAX_SIZE)
199
0
  {
200
0
    return;
201
0
  }
202
203
3.92k
  old_size = this->size;
204
3.92k
  old_table = this->table;
205
206
3.92k
  init_hashtable(this, old_size << 1);
207
208
254k
  for (row = 0; row < old_size; row++)
209
250k
  {
210
250k
    to_move = old_table[row];
211
439k
    while (to_move)
212
188k
    {
213
188k
      pair_t *prev = NULL;
214
215
188k
      pair = this->table[to_move->hash & this->mask];
216
227k
      while (pair)
217
39.2k
      {
218
39.2k
        if (this->cmp && this->cmp(to_move->key, pair->key) < 0)
219
0
        {
220
0
          break;
221
0
        }
222
39.2k
        prev = pair;
223
39.2k
        pair = pair->next;
224
39.2k
      }
225
188k
      next = to_move->next;
226
188k
      insert_pair(this, to_move, prev);
227
188k
      to_move = next;
228
188k
    }
229
250k
  }
230
3.92k
  free(old_table);
231
3.92k
}
232
233
/**
234
 * Find the pair with the given key, optionally returning the hash and previous
235
 * (or last) pair in the bucket.
236
 */
237
static inline pair_t *find_key(private_hashlist_t *this, const void *key,
238
                 hashtable_equals_t equals, u_int *out_hash,
239
                 pair_t **out_prev)
240
2.37M
{
241
2.37M
  pair_t *pair, *prev = NULL;
242
2.37M
  bool use_callback = equals != NULL;
243
2.37M
  u_int hash;
244
245
2.37M
  if (!this->count && !out_hash)
246
3.92k
  { /* no need to calculate the hash if not requested */
247
3.92k
    return NULL;
248
3.92k
  }
249
250
2.36M
  equals = equals ?: this->equals;
251
2.36M
  hash = this->hash(key);
252
2.36M
  if (out_hash)
253
297k
  {
254
297k
    *out_hash = hash;
255
297k
  }
256
257
2.36M
  lookup_start();
258
259
2.36M
  pair = this->table[hash & this->mask];
260
4.39M
  while (pair)
261
2.90M
  {
262
2.90M
    lookup_probing();
263
    /* when keys are sorted, we compare all items so we can abort earlier
264
     * even if the hash does not match, but only as long as we don't
265
     * have a callback */
266
2.90M
    if (!use_callback && this->cmp)
267
0
    {
268
0
      int cmp = this->cmp(key, pair->key);
269
0
      if (cmp == 0)
270
0
      {
271
0
        break;
272
0
      }
273
0
      else if (cmp < 0)
274
0
      { /* no need to continue as the key we search is smaller */
275
0
        pair = NULL;
276
0
        break;
277
0
      }
278
0
    }
279
2.90M
    else if (hash == pair->hash && equals(key, pair->key))
280
870k
    {
281
870k
      break;
282
870k
    }
283
2.03M
    prev = pair;
284
2.03M
    pair = pair->next;
285
2.03M
  }
286
2.36M
  if (out_prev)
287
595k
  {
288
595k
    *out_prev = prev;
289
595k
  }
290
2.36M
  if (pair)
291
870k
  {
292
870k
    lookup_success(&this->profile);
293
870k
  }
294
1.49M
  else
295
1.49M
  {
296
1.49M
    lookup_failure(&this->profile);
297
1.49M
  }
298
2.36M
  return pair;
299
2.37M
}
300
301
METHOD(hashtable_t, put, void*,
302
  private_hashlist_t *this, const void *key, void *value)
303
297k
{
304
297k
  void *old_value = NULL;
305
297k
  pair_t *pair, *prev = NULL;
306
297k
  u_int hash;
307
308
297k
  if (this->count >= this->size * LOAD_FACTOR)
309
3.92k
  {
310
3.92k
    rehash(this);
311
3.92k
  }
312
313
297k
  pair = find_key(this, key, NULL, &hash, &prev);
314
297k
  if (pair)
315
0
  {
316
0
    old_value = pair->value;
317
0
    pair->value = value;
318
0
    pair->key = key;
319
0
  }
320
297k
  else
321
297k
  {
322
297k
    INIT(pair,
323
297k
      .key = key,
324
297k
      .value = value,
325
297k
      .hash = hash,
326
297k
    );
327
297k
    insert_pair(this, pair, prev);
328
297k
    this->count++;
329
297k
    profile_count(&this->profile, this->count);
330
297k
  }
331
297k
  return old_value;
332
297k
}
333
334
335
METHOD(hashtable_t, get, void*,
336
  private_hashlist_t *this, const void *key)
337
690k
{
338
690k
  pair_t *pair = find_key(this, key, NULL, NULL, NULL);
339
690k
  return pair ? pair->value : NULL;
340
690k
}
341
342
METHOD(hashlist_t, get_match, void*,
343
  private_hashlist_t *this, const void *key, hashtable_equals_t match)
344
1.08M
{
345
1.08M
  pair_t *pair = find_key(this, key, match, NULL, NULL);
346
1.08M
  return pair ? pair->value : NULL;
347
1.08M
}
348
349
METHOD(hashtable_t, remove_, void*,
350
  private_hashlist_t *this, const void *key)
351
297k
{
352
297k
  void *value = NULL;
353
297k
  pair_t *pair, *prev = NULL;
354
355
297k
  pair = find_key(this, key, NULL, NULL, &prev);
356
297k
  if (pair)
357
297k
  {
358
297k
    if (prev)
359
50.9k
    {
360
50.9k
      prev->next = pair->next;
361
50.9k
    }
362
247k
    else
363
247k
    {
364
247k
      this->table[pair->hash & this->mask] = pair->next;
365
247k
    }
366
297k
    value = pair->value;
367
297k
    free(pair);
368
297k
    this->count--;
369
297k
  }
370
297k
  return value;
371
297k
}
372
373
METHOD(hashtable_t, remove_at, void,
374
  private_hashlist_t *this, private_enumerator_t *enumerator)
375
0
{
376
0
  if (enumerator->table == this && enumerator->current)
377
0
  {
378
0
    pair_t *current = enumerator->current;
379
0
    if (enumerator->prev)
380
0
    {
381
0
      enumerator->prev->next = current->next;
382
0
    }
383
0
    else
384
0
    {
385
0
      this->table[enumerator->row] = current->next;
386
0
    }
387
0
    enumerator->current = enumerator->prev;
388
0
    free(current);
389
0
    this->count--;
390
0
  }
391
0
}
392
393
METHOD(hashtable_t, get_count, u_int,
394
  private_hashlist_t *this)
395
0
{
396
0
  return this->count;
397
0
}
398
399
METHOD(enumerator_t, enumerate, bool,
400
  private_enumerator_t *this, va_list args)
401
0
{
402
0
  const void **key;
403
0
  void **value;
404
405
0
  VA_ARGS_VGET(args, key, value);
406
407
0
  while (this->count && this->row < this->table->size)
408
0
  {
409
0
    this->prev = this->current;
410
0
    if (this->current)
411
0
    {
412
0
      this->current = this->current->next;
413
0
    }
414
0
    else
415
0
    {
416
0
      this->current = this->table->table[this->row];
417
0
    }
418
0
    if (this->current)
419
0
    {
420
0
      if (key)
421
0
      {
422
0
        *key = this->current->key;
423
0
      }
424
0
      if (value)
425
0
      {
426
0
        *value = this->current->value;
427
0
      }
428
0
      this->count--;
429
0
      return TRUE;
430
0
    }
431
0
    this->row++;
432
0
  }
433
0
  return FALSE;
434
0
}
435
436
METHOD(hashtable_t, create_enumerator, enumerator_t*,
437
  private_hashlist_t *this)
438
0
{
439
0
  private_enumerator_t *enumerator;
440
441
0
  INIT(enumerator,
442
0
    .enumerator = {
443
0
      .enumerate = enumerator_enumerate_default,
444
0
      .venumerate = _enumerate,
445
0
      .destroy = (void*)free,
446
0
    },
447
0
    .table = this,
448
0
    .count = this->count,
449
0
  );
450
451
0
  return &enumerator->enumerator;
452
0
}
453
454
static void destroy_internal(private_hashlist_t *this,
455
               void (*fn)(void*,const void*))
456
3.92k
{
457
3.92k
  pair_t *pair, *next;
458
3.92k
  u_int row;
459
460
3.92k
  profiler_cleanup(&this->profile, this->count, this->size);
461
462
505k
  for (row = 0; row < this->size; row++)
463
501k
  {
464
501k
    pair = this->table[row];
465
501k
    while (pair)
466
0
    {
467
0
      if (fn)
468
0
      {
469
0
        fn(pair->value, pair->key);
470
0
      }
471
0
      next = pair->next;
472
0
      free(pair);
473
0
      pair = next;
474
0
    }
475
501k
  }
476
3.92k
  free(this->table);
477
3.92k
  free(this);
478
3.92k
}
479
480
METHOD2(hashlist_t, hashtable_t, destroy, void,
481
  private_hashlist_t *this)
482
3.92k
{
483
3.92k
  destroy_internal(this, NULL);
484
3.92k
}
485
486
METHOD(hashtable_t, destroy_function, void,
487
  private_hashlist_t *this, void (*fn)(void*,const void*))
488
0
{
489
0
  destroy_internal(this, fn);
490
0
}
491
492
/**
493
 * Create a hash list
494
 */
495
static private_hashlist_t *hashlist_create_internal(hashtable_hash_t hash,
496
                          u_int size)
497
3.92k
{
498
3.92k
  private_hashlist_t *this;
499
500
3.92k
  INIT(this,
501
3.92k
    .public = {
502
3.92k
      .ht = {
503
3.92k
        .put = _put,
504
3.92k
        .get = _get,
505
3.92k
        .remove = _remove_,
506
3.92k
        .remove_at = (void*)_remove_at,
507
3.92k
        .get_count = _get_count,
508
3.92k
        .create_enumerator = _create_enumerator,
509
3.92k
        .destroy = _destroy,
510
3.92k
        .destroy_function = _destroy_function,
511
3.92k
      },
512
3.92k
      .get_match = _get_match,
513
3.92k
      .destroy = _destroy,
514
3.92k
    },
515
3.92k
    .hash = hash,
516
3.92k
  );
517
518
3.92k
  init_hashtable(this, size);
519
520
3.92k
  profiler_init(&this->profile, 3);
521
522
3.92k
  return this;
523
3.92k
}
524
525
/*
526
 * Described in header
527
 */
528
hashlist_t *hashlist_create(hashtable_hash_t hash, hashtable_equals_t equals,
529
              u_int size)
530
3.92k
{
531
3.92k
  private_hashlist_t *this = hashlist_create_internal(hash, size);
532
533
3.92k
  this->equals = equals;
534
535
3.92k
  return &this->public;
536
3.92k
}
537
538
/*
539
 * Described in header
540
 */
541
hashlist_t *hashlist_create_sorted(hashtable_hash_t hash,
542
                   hashtable_cmp_t cmp, u_int size)
543
0
{
544
0
  private_hashlist_t *this = hashlist_create_internal(hash, size);
545
546
0
  this->cmp = cmp;
547
548
0
  return &this->public;
549
0
}