Coverage Report

Created: 2026-09-08 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/hash.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/* Hash routine.
3
 * Copyright (C) 1998 Kunihiro Ishiguro
4
 */
5
6
#include <zebra.h>
7
#include <math.h>
8
9
#include "hash.h"
10
#include "memory.h"
11
#include "linklist.h"
12
#include "termtable.h"
13
#include "vty.h"
14
#include "command.h"
15
#include "libfrr.h"
16
#include "frr_pthread.h"
17
#include "libfrr_trace.h"
18
19
8
DEFINE_MTYPE_STATIC(LIB, HASH, "Hash");
20
8
DEFINE_MTYPE_STATIC(LIB, HASH_BUCKET, "Hash Bucket");
21
8
DEFINE_MTYPE_STATIC(LIB, HASH_INDEX, "Hash Index");
22
8
23
8
static pthread_mutex_t _hashes_mtx = PTHREAD_MUTEX_INITIALIZER;
24
8
static struct list *_hashes;
25
8
26
8
struct hash *hash_create_size(unsigned int size,
27
8
            unsigned int (*hash_key)(const void *),
28
8
            bool (*hash_cmp)(const void *, const void *),
29
8
            const char *name)
30
46.0k
{
31
46.0k
  struct hash *hash;
32
33
46.0k
  assert((size & (size - 1)) == 0);
34
46.0k
  hash = XCALLOC(MTYPE_HASH, sizeof(struct hash));
35
46.0k
  hash->index =
36
46.0k
    XCALLOC(MTYPE_HASH_INDEX, sizeof(struct hash_bucket *) * size);
37
46.0k
  hash->size = size;
38
46.0k
  hash->hash_key = hash_key;
39
46.0k
  hash->hash_cmp = hash_cmp;
40
46.0k
  hash->count = 0;
41
46.0k
  hash->name = name ? XSTRDUP(MTYPE_HASH, name) : NULL;
42
46.0k
  hash->stats.empty = hash->size;
43
44
46.0k
  frr_with_mutex (&_hashes_mtx) {
45
46.0k
    if (!_hashes)
46
4
      _hashes = list_new();
47
48
46.0k
    listnode_add(_hashes, hash);
49
46.0k
  }
50
51
46.0k
  return hash;
52
46.0k
}
53
54
struct hash *hash_create(unsigned int (*hash_key)(const void *),
55
       bool (*hash_cmp)(const void *, const void *),
56
       const char *name)
57
49
{
58
49
  return hash_create_size(HASH_INITIAL_SIZE, hash_key, hash_cmp, name);
59
49
}
60
61
void *hash_alloc_intern(void *arg)
62
38.4k
{
63
38.4k
  return arg;
64
38.4k
}
65
66
/*
67
 * ssq = ssq + (new^2 - old^2)
68
 *     = ssq + ((new + old) * (new - old))
69
 */
70
#define hash_update_ssq(hz, old, new)                                          \
71
92.2k
  do {                                                                   \
72
92.2k
    int _adjust = (new + old) * (new - old);                       \
73
92.2k
    if (_adjust < 0)                                               \
74
92.2k
      atomic_fetch_sub_explicit(&hz->stats.ssq, -_adjust,    \
75
41.6k
              memory_order_relaxed);       \
76
92.2k
    else                                                           \
77
92.2k
      atomic_fetch_add_explicit(&hz->stats.ssq, _adjust,     \
78
50.5k
              memory_order_relaxed);       \
79
92.2k
  } while (0)
80
81
/* Expand hash if the chain length exceeds the threshold. */
82
static void hash_expand(struct hash *hash)
83
32
{
84
32
  unsigned int i, new_size;
85
32
  struct hash_bucket *hb, *hbnext, **new_index;
86
87
32
  new_size = hash->size * 2;
88
89
32
  if (hash->max_size && new_size > hash->max_size)
90
0
    return;
91
92
32
  new_index = XCALLOC(MTYPE_HASH_INDEX,
93
32
          sizeof(struct hash_bucket *) * new_size);
94
95
32
  hash->stats.empty = new_size;
96
97
4.72k
  for (i = 0; i < hash->size; i++)
98
9.39k
    for (hb = hash->index[i]; hb; hb = hbnext) {
99
4.69k
      unsigned int h = hb->key & (new_size - 1);
100
101
4.69k
      hbnext = hb->next;
102
4.69k
      hb->next = new_index[h];
103
104
4.69k
      int oldlen = hb->next ? hb->next->len : 0;
105
4.69k
      int newlen = oldlen + 1;
106
107
4.69k
      if (newlen == 1)
108
3.64k
        hash->stats.empty--;
109
1.04k
      else
110
1.04k
        hb->next->len = 0;
111
112
4.69k
      hb->len = newlen;
113
114
4.69k
      hash_update_ssq(hash, oldlen, newlen);
115
116
4.69k
      new_index[h] = hb;
117
4.69k
    }
118
119
  /* Switch to new table */
120
32
  XFREE(MTYPE_HASH_INDEX, hash->index);
121
32
  hash->size = new_size;
122
32
  hash->index = new_index;
123
32
}
124
125
void *hash_get(struct hash *hash, void *data, void *(*alloc_func)(void *))
126
273k
{
127
273k
  frrtrace(2, frr_libfrr, hash_get, hash, data);
128
129
273k
  unsigned int key;
130
273k
  unsigned int index;
131
273k
  void *newdata;
132
273k
  struct hash_bucket *bucket;
133
134
273k
  if (!alloc_func && !hash->count)
135
47.1k
    return NULL;
136
137
226k
  key = (*hash->hash_key)(data);
138
226k
  index = key & (hash->size - 1);
139
140
307k
  for (bucket = hash->index[index]; bucket != NULL;
141
235k
       bucket = bucket->next) {
142
235k
    if (bucket->key == key && (*hash->hash_cmp)(bucket->data, data))
143
154k
      return bucket->data;
144
235k
  }
145
146
71.3k
  if (alloc_func) {
147
45.8k
    newdata = (*alloc_func)(data);
148
45.8k
    if (newdata == NULL)
149
0
      return NULL;
150
151
45.8k
    if (HASH_THRESHOLD(hash->count + 1, hash->size)) {
152
32
      hash_expand(hash);
153
32
      index = key & (hash->size - 1);
154
32
    }
155
156
45.8k
    bucket = XCALLOC(MTYPE_HASH_BUCKET, sizeof(struct hash_bucket));
157
45.8k
    bucket->data = newdata;
158
45.8k
    bucket->key = key;
159
45.8k
    bucket->next = hash->index[index];
160
45.8k
    hash->index[index] = bucket;
161
45.8k
    hash->count++;
162
163
45.8k
    frrtrace(3, frr_libfrr, hash_insert, hash, data, key);
164
165
45.8k
    int oldlen = bucket->next ? bucket->next->len : 0;
166
45.8k
    int newlen = oldlen + 1;
167
168
45.8k
    if (newlen == 1)
169
33.9k
      hash->stats.empty--;
170
11.9k
    else
171
11.9k
      bucket->next->len = 0;
172
173
45.8k
    bucket->len = newlen;
174
175
45.8k
    hash_update_ssq(hash, oldlen, newlen);
176
177
45.8k
    return bucket->data;
178
45.8k
  }
179
25.5k
  return NULL;
180
71.3k
}
181
182
void *hash_lookup(struct hash *hash, void *data)
183
208k
{
184
208k
  return hash_get(hash, data, NULL);
185
208k
}
186
187
unsigned int string_hash_make(const char *str)
188
0
{
189
0
  unsigned int hash = 0;
190
191
0
  while (*str)
192
0
    hash = (hash * 33) ^ (unsigned int)*str++;
193
194
0
  return hash;
195
0
}
196
197
void *hash_release(struct hash *hash, void *data)
198
42.3k
{
199
42.3k
  void *ret = NULL;
200
42.3k
  unsigned int key;
201
42.3k
  unsigned int index;
202
42.3k
  struct hash_bucket *bucket;
203
42.3k
  struct hash_bucket *pp;
204
205
42.3k
  key = (*hash->hash_key)(data);
206
42.3k
  index = key & (hash->size - 1);
207
208
43.0k
  for (bucket = pp = hash->index[index]; bucket; bucket = bucket->next) {
209
42.3k
    if (bucket->key == key
210
41.6k
        && (*hash->hash_cmp)(bucket->data, data)) {
211
41.6k
      int oldlen = hash->index[index]->len;
212
41.6k
      int newlen = oldlen - 1;
213
214
41.6k
      if (bucket == pp)
215
41.0k
        hash->index[index] = bucket->next;
216
578
      else
217
578
        pp->next = bucket->next;
218
219
41.6k
      if (hash->index[index])
220
10.0k
        hash->index[index]->len = newlen;
221
31.5k
      else
222
31.5k
        hash->stats.empty++;
223
224
41.6k
      hash_update_ssq(hash, oldlen, newlen);
225
226
41.6k
      ret = bucket->data;
227
41.6k
      XFREE(MTYPE_HASH_BUCKET, bucket);
228
41.6k
      hash->count--;
229
41.6k
      break;
230
41.6k
    }
231
631
    pp = bucket;
232
631
  }
233
234
42.3k
  frrtrace(3, frr_libfrr, hash_release, hash, data, ret);
235
236
42.3k
  return ret;
237
42.3k
}
238
239
void hash_iterate(struct hash *hash, void (*func)(struct hash_bucket *, void *),
240
      void *arg)
241
1.35k
{
242
1.35k
  unsigned int i;
243
1.35k
  struct hash_bucket *hb;
244
1.35k
  struct hash_bucket *hbnext;
245
246
54.9k
  for (i = 0; i < hash->size; i++)
247
57.0k
    for (hb = hash->index[i]; hb; hb = hbnext) {
248
      /* get pointer to next hash bucket here, in case (*func)
249
       * decides to delete hb by calling hash_release
250
       */
251
3.42k
      hbnext = hb->next;
252
3.42k
      (*func)(hb, arg);
253
3.42k
    }
254
1.35k
}
255
256
void hash_walk(struct hash *hash, int (*func)(struct hash_bucket *, void *),
257
         void *arg)
258
3.22k
{
259
3.22k
  unsigned int i;
260
3.22k
  struct hash_bucket *hb;
261
3.22k
  struct hash_bucket *hbnext;
262
3.22k
  int ret = HASHWALK_CONTINUE;
263
264
163k
  for (i = 0; i < hash->size; i++) {
265
195k
    for (hb = hash->index[i]; hb; hb = hbnext) {
266
      /* get pointer to next hash bucket here, in case (*func)
267
       * decides to delete hb by calling hash_release
268
       */
269
36.0k
      hbnext = hb->next;
270
36.0k
      ret = (*func)(hb, arg);
271
36.0k
      if (ret == HASHWALK_ABORT)
272
304
        return;
273
36.0k
    }
274
160k
  }
275
3.22k
}
276
277
void hash_clean(struct hash *hash, void (*free_func)(void *))
278
27.5k
{
279
27.5k
  unsigned int i;
280
27.5k
  struct hash_bucket *hb;
281
27.5k
  struct hash_bucket *next;
282
283
908k
  for (i = 0; i < hash->size; i++) {
284
881k
    for (hb = hash->index[i]; hb; hb = next) {
285
0
      next = hb->next;
286
287
0
      if (free_func)
288
0
        (*free_func)(hb->data);
289
290
0
      XFREE(MTYPE_HASH_BUCKET, hb);
291
0
      hash->count--;
292
0
    }
293
881k
    hash->index[i] = NULL;
294
881k
  }
295
296
27.5k
  hash->stats.ssq = 0;
297
27.5k
  hash->stats.empty = hash->size;
298
27.5k
}
299
300
void hash_clean_and_free(struct hash **hash, void (*free_func)(void *))
301
27.5k
{
302
27.5k
  if (!*hash)
303
0
    return;
304
305
27.5k
  hash_clean(*hash, free_func);
306
27.5k
  hash_free(*hash);
307
27.5k
  *hash = NULL;
308
27.5k
}
309
310
static void hash_to_list_iter(struct hash_bucket *hb, void *arg)
311
0
{
312
0
  struct list *list = arg;
313
314
0
  listnode_add(list, hb->data);
315
0
}
316
317
struct list *hash_to_list(struct hash *hash)
318
0
{
319
0
  struct list *list = list_new();
320
321
0
  hash_iterate(hash, hash_to_list_iter, list);
322
0
  return list;
323
0
}
324
325
void hash_free(struct hash *hash)
326
45.2k
{
327
45.2k
  frr_with_mutex (&_hashes_mtx) {
328
45.2k
    if (_hashes) {
329
45.2k
      listnode_delete(_hashes, hash);
330
45.2k
      if (_hashes->count == 0) {
331
0
        list_delete(&_hashes);
332
0
      }
333
45.2k
    }
334
45.2k
  }
335
336
45.2k
  XFREE(MTYPE_HASH, hash->name);
337
338
45.2k
  XFREE(MTYPE_HASH_INDEX, hash->index);
339
45.2k
  XFREE(MTYPE_HASH, hash);
340
45.2k
}
341
342
343
/* CLI commands ------------------------------------------------------------ */
344
345
DEFUN_NOSH(show_hash_stats,
346
           show_hash_stats_cmd,
347
           "show debugging hashtable [statistics]",
348
           SHOW_STR
349
           DEBUG_STR
350
           "Statistics about hash tables\n"
351
           "Statistics about hash tables\n")
352
0
{
353
0
  struct hash *h;
354
0
  struct listnode *ln;
355
0
  struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
356
357
0
  ttable_add_row(tt, "Hash table|Buckets|Entries|Empty|LF|SD|FLF|SD");
358
0
  tt->style.cell.lpad = 2;
359
0
  tt->style.cell.rpad = 1;
360
0
  tt->style.corner = '+';
361
0
  ttable_restyle(tt);
362
0
  ttable_rowseps(tt, 0, BOTTOM, true, '-');
363
364
  /* Summary statistics calculated are:
365
   *
366
   * - Load factor: This is the number of elements in the table divided
367
   *   by the number of buckets. Since this hash table implementation
368
   *   uses chaining, this value can be greater than 1.
369
   *   This number provides information on how 'full' the table is, but
370
   *   does not provide information on how evenly distributed the
371
   *   elements are.
372
   *   Notably, a load factor >= 1 does not imply that every bucket has
373
   *   an element; with a pathological hash function, all elements could
374
   *   be in a single bucket.
375
   *
376
   * - Full load factor: this is the number of elements in the table
377
   *   divided by the number of buckets that have some elements in them.
378
   *
379
   * - Std. Dev.: This is the standard deviation calculated from the
380
   *   relevant load factor. If the load factor is the mean of number of
381
   *   elements per bucket, the standard deviation measures how much any
382
   *   particular bucket is likely to deviate from the mean.
383
   *   As a rule of thumb this number should be less than 2, and ideally
384
   *   <= 1 for optimal performance. A number larger than 3 generally
385
   *   indicates a poor hash function.
386
   */
387
388
0
  double lf;    // load factor
389
0
  double flf;   // full load factor
390
0
  double var;   // overall variance
391
0
  double fvar;  // full variance
392
0
  double stdv;  // overall stddev
393
0
  double fstdv; // full stddev
394
395
0
  long double x2;   // h->count ^ 2
396
0
  long double ldc;  // (long double) h->count
397
0
  long double full; // h->size - h->stats.empty
398
0
  long double ssq;  // ssq casted to long double
399
400
0
  pthread_mutex_lock(&_hashes_mtx);
401
0
  if (!_hashes) {
402
0
    pthread_mutex_unlock(&_hashes_mtx);
403
0
    ttable_del(tt);
404
0
    vty_out(vty, "No hash tables in use.\n");
405
0
    return CMD_SUCCESS;
406
0
  }
407
408
0
  for (ALL_LIST_ELEMENTS_RO(_hashes, ln, h)) {
409
0
    if (!h->name)
410
0
      continue;
411
412
0
    ssq = (long double)h->stats.ssq;
413
0
    x2 = h->count * h->count;
414
0
    ldc = (long double)h->count;
415
0
    full = h->size - h->stats.empty;
416
0
    lf = h->count / (double)h->size;
417
0
    flf = full ? h->count / (double)(full) : 0;
418
0
    var = ldc ? (1.0 / ldc) * (ssq - x2 / ldc) : 0;
419
0
    fvar = full ? (1.0 / full) * (ssq - x2 / full) : 0;
420
0
    var = (var < .0001) ? 0 : var;
421
0
    fvar = (fvar < .0001) ? 0 : fvar;
422
0
    stdv = sqrt(var);
423
0
    fstdv = sqrt(fvar);
424
425
0
    ttable_add_row(tt, "%s|%d|%ld|%.0f%%|%.2lf|%.2lf|%.2lf|%.2lf",
426
0
             h->name, h->size, h->count,
427
0
             (h->stats.empty / (double)h->size) * 100, lf,
428
0
             stdv, flf, fstdv);
429
0
  }
430
0
  pthread_mutex_unlock(&_hashes_mtx);
431
432
  /* display header */
433
0
  char header[] = "Showing hash table statistics for ";
434
0
  char underln[sizeof(header) + strlen(frr_protonameinst)];
435
0
  memset(underln, '-', sizeof(underln));
436
0
  underln[sizeof(underln) - 1] = '\0';
437
0
  vty_out(vty, "%s%s\n", header, frr_protonameinst);
438
0
  vty_out(vty, "%s\n", underln);
439
440
0
  vty_out(vty, "# allocated: %d\n", _hashes->count);
441
0
  vty_out(vty, "# named:     %d\n\n", tt->nrows - 1);
442
443
0
  if (tt->nrows > 1) {
444
0
    ttable_colseps(tt, 0, RIGHT, true, '|');
445
0
    char *table = ttable_dump(tt, "\n");
446
0
    vty_out(vty, "%s\n", table);
447
0
    XFREE(MTYPE_TMP, table);
448
0
  } else
449
0
    vty_out(vty, "No named hash tables to display.\n");
450
451
0
  ttable_del(tt);
452
453
0
  return CMD_SUCCESS;
454
0
}
455
456
void hash_cmd_init(void)
457
4
{
458
4
  install_element(ENABLE_NODE, &show_hash_stats_cmd);
459
4
}