Coverage Report

Created: 2026-07-30 06:41

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
57.4k
{
31
57.4k
  struct hash *hash;
32
33
57.4k
  assert((size & (size - 1)) == 0);
34
57.4k
  hash = XCALLOC(MTYPE_HASH, sizeof(struct hash));
35
57.4k
  hash->index =
36
57.4k
    XCALLOC(MTYPE_HASH_INDEX, sizeof(struct hash_bucket *) * size);
37
57.4k
  hash->size = size;
38
57.4k
  hash->hash_key = hash_key;
39
57.4k
  hash->hash_cmp = hash_cmp;
40
57.4k
  hash->count = 0;
41
57.4k
  hash->name = name ? XSTRDUP(MTYPE_HASH, name) : NULL;
42
57.4k
  hash->stats.empty = hash->size;
43
44
57.4k
  frr_with_mutex (&_hashes_mtx) {
45
57.4k
    if (!_hashes)
46
4
      _hashes = list_new();
47
48
57.4k
    listnode_add(_hashes, hash);
49
57.4k
  }
50
51
57.4k
  return hash;
52
57.4k
}
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
82.5k
{
63
82.5k
  return arg;
64
82.5k
}
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
178k
  do {                                                                   \
72
178k
    int _adjust = (new + old) * (new - old);                       \
73
178k
    if (_adjust < 0)                                               \
74
178k
      atomic_fetch_sub_explicit(&hz->stats.ssq, -_adjust,    \
75
84.8k
              memory_order_relaxed);       \
76
178k
    else                                                           \
77
178k
      atomic_fetch_add_explicit(&hz->stats.ssq, _adjust,     \
78
93.7k
              memory_order_relaxed);       \
79
178k
  } 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.72k
        hash->stats.empty--;
109
976
      else
110
976
        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
541k
{
127
541k
  frrtrace(2, frr_libfrr, hash_get, hash, data);
128
129
541k
  unsigned int key;
130
541k
  unsigned int index;
131
541k
  void *newdata;
132
541k
  struct hash_bucket *bucket;
133
134
541k
  if (!alloc_func && !hash->count)
135
74.3k
    return NULL;
136
137
467k
  key = (*hash->hash_key)(data);
138
467k
  index = key & (hash->size - 1);
139
140
656k
  for (bucket = hash->index[index]; bucket != NULL;
141
528k
       bucket = bucket->next) {
142
528k
    if (bucket->key == key && (*hash->hash_cmp)(bucket->data, data))
143
338k
      return bucket->data;
144
528k
  }
145
146
128k
  if (alloc_func) {
147
89.0k
    newdata = (*alloc_func)(data);
148
89.0k
    if (newdata == NULL)
149
0
      return NULL;
150
151
89.0k
    if (HASH_THRESHOLD(hash->count + 1, hash->size)) {
152
32
      hash_expand(hash);
153
32
      index = key & (hash->size - 1);
154
32
    }
155
156
89.0k
    bucket = XCALLOC(MTYPE_HASH_BUCKET, sizeof(struct hash_bucket));
157
89.0k
    bucket->data = newdata;
158
89.0k
    bucket->key = key;
159
89.0k
    bucket->next = hash->index[index];
160
89.0k
    hash->index[index] = bucket;
161
89.0k
    hash->count++;
162
163
89.0k
    frrtrace(3, frr_libfrr, hash_insert, hash, data, key);
164
165
89.0k
    int oldlen = bucket->next ? bucket->next->len : 0;
166
89.0k
    int newlen = oldlen + 1;
167
168
89.0k
    if (newlen == 1)
169
67.1k
      hash->stats.empty--;
170
21.9k
    else
171
21.9k
      bucket->next->len = 0;
172
173
89.0k
    bucket->len = newlen;
174
175
89.0k
    hash_update_ssq(hash, oldlen, newlen);
176
177
89.0k
    return bucket->data;
178
89.0k
  }
179
39.2k
  return NULL;
180
128k
}
181
182
void *hash_lookup(struct hash *hash, void *data)
183
417k
{
184
417k
  return hash_get(hash, data, NULL);
185
417k
}
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
87.6k
{
199
87.6k
  void *ret = NULL;
200
87.6k
  unsigned int key;
201
87.6k
  unsigned int index;
202
87.6k
  struct hash_bucket *bucket;
203
87.6k
  struct hash_bucket *pp;
204
205
87.6k
  key = (*hash->hash_key)(data);
206
87.6k
  index = key & (hash->size - 1);
207
208
90.2k
  for (bucket = pp = hash->index[index]; bucket; bucket = bucket->next) {
209
87.4k
    if (bucket->key == key
210
84.8k
        && (*hash->hash_cmp)(bucket->data, data)) {
211
84.8k
      int oldlen = hash->index[index]->len;
212
84.8k
      int newlen = oldlen - 1;
213
214
84.8k
      if (bucket == pp)
215
82.4k
        hash->index[index] = bucket->next;
216
2.40k
      else
217
2.40k
        pp->next = bucket->next;
218
219
84.8k
      if (hash->index[index])
220
20.0k
        hash->index[index]->len = newlen;
221
64.7k
      else
222
64.7k
        hash->stats.empty++;
223
224
84.8k
      hash_update_ssq(hash, oldlen, newlen);
225
226
84.8k
      ret = bucket->data;
227
84.8k
      XFREE(MTYPE_HASH_BUCKET, bucket);
228
84.8k
      hash->count--;
229
84.8k
      break;
230
84.8k
    }
231
2.59k
    pp = bucket;
232
2.59k
  }
233
234
87.6k
  frrtrace(3, frr_libfrr, hash_release, hash, data, ret);
235
236
87.6k
  return ret;
237
87.6k
}
238
239
void hash_iterate(struct hash *hash, void (*func)(struct hash_bucket *, void *),
240
      void *arg)
241
1.29k
{
242
1.29k
  unsigned int i;
243
1.29k
  struct hash_bucket *hb;
244
1.29k
  struct hash_bucket *hbnext;
245
246
52.6k
  for (i = 0; i < hash->size; i++)
247
54.3k
    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.04k
      hbnext = hb->next;
252
3.04k
      (*func)(hb, arg);
253
3.04k
    }
254
1.29k
}
255
256
void hash_walk(struct hash *hash, int (*func)(struct hash_bucket *, void *),
257
         void *arg)
258
3.08k
{
259
3.08k
  unsigned int i;
260
3.08k
  struct hash_bucket *hb;
261
3.08k
  struct hash_bucket *hbnext;
262
3.08k
  int ret = HASHWALK_CONTINUE;
263
264
141k
  for (i = 0; i < hash->size; i++) {
265
168k
    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
30.0k
      hbnext = hb->next;
270
30.0k
      ret = (*func)(hb, arg);
271
30.0k
      if (ret == HASHWALK_ABORT)
272
191
        return;
273
30.0k
    }
274
138k
  }
275
3.08k
}
276
277
void hash_clean(struct hash *hash, void (*free_func)(void *))
278
26.3k
{
279
26.3k
  unsigned int i;
280
26.3k
  struct hash_bucket *hb;
281
26.3k
  struct hash_bucket *next;
282
283
868k
  for (i = 0; i < hash->size; i++) {
284
842k
    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
842k
    hash->index[i] = NULL;
294
842k
  }
295
296
26.3k
  hash->stats.ssq = 0;
297
26.3k
  hash->stats.empty = hash->size;
298
26.3k
}
299
300
void hash_clean_and_free(struct hash **hash, void (*free_func)(void *))
301
26.3k
{
302
26.3k
  if (!*hash)
303
0
    return;
304
305
26.3k
  hash_clean(*hash, free_func);
306
26.3k
  hash_free(*hash);
307
26.3k
  *hash = NULL;
308
26.3k
}
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
56.5k
{
327
56.5k
  frr_with_mutex (&_hashes_mtx) {
328
56.5k
    if (_hashes) {
329
56.5k
      listnode_delete(_hashes, hash);
330
56.5k
      if (_hashes->count == 0) {
331
0
        list_delete(&_hashes);
332
0
      }
333
56.5k
    }
334
56.5k
  }
335
336
56.5k
  XFREE(MTYPE_HASH, hash->name);
337
338
56.5k
  XFREE(MTYPE_HASH_INDEX, hash->index);
339
56.5k
  XFREE(MTYPE_HASH, hash);
340
56.5k
}
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
}