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