18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * A hash table (hashtab) maintains associations between
48c2ecf20Sopenharmony_ci * key values and datum values.  The type of the key values
58c2ecf20Sopenharmony_ci * and the type of the datum values is arbitrary.  The
68c2ecf20Sopenharmony_ci * functions for hash computation and key comparison are
78c2ecf20Sopenharmony_ci * provided by the creator of the table.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Author : Stephen Smalley, <sds@tycho.nsa.gov>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci#ifndef _SS_HASHTAB_H_
128c2ecf20Sopenharmony_ci#define _SS_HASHTAB_H_
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/types.h>
158c2ecf20Sopenharmony_ci#include <linux/errno.h>
168c2ecf20Sopenharmony_ci#include <linux/sched.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define HASHTAB_MAX_NODES	U32_MAX
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct hashtab_key_params {
218c2ecf20Sopenharmony_ci	u32 (*hash)(const void *key);	/* hash function */
228c2ecf20Sopenharmony_ci	int (*cmp)(const void *key1, const void *key2);
238c2ecf20Sopenharmony_ci					/* key comparison function */
248c2ecf20Sopenharmony_ci};
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistruct hashtab_node {
278c2ecf20Sopenharmony_ci	void *key;
288c2ecf20Sopenharmony_ci	void *datum;
298c2ecf20Sopenharmony_ci	struct hashtab_node *next;
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct hashtab {
338c2ecf20Sopenharmony_ci	struct hashtab_node **htable;	/* hash table */
348c2ecf20Sopenharmony_ci	u32 size;			/* number of slots in hash table */
358c2ecf20Sopenharmony_ci	u32 nel;			/* number of elements in hash table */
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct hashtab_info {
398c2ecf20Sopenharmony_ci	u32 slots_used;
408c2ecf20Sopenharmony_ci	u32 max_chain_len;
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/*
448c2ecf20Sopenharmony_ci * Initializes a new hash table with the specified characteristics.
458c2ecf20Sopenharmony_ci *
468c2ecf20Sopenharmony_ci * Returns -ENOMEM if insufficient space is available or 0 otherwise.
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_ciint hashtab_init(struct hashtab *h, u32 nel_hint);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ciint __hashtab_insert(struct hashtab *h, struct hashtab_node **dst,
518c2ecf20Sopenharmony_ci		     void *key, void *datum);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/*
548c2ecf20Sopenharmony_ci * Inserts the specified (key, datum) pair into the specified hash table.
558c2ecf20Sopenharmony_ci *
568c2ecf20Sopenharmony_ci * Returns -ENOMEM on memory allocation error,
578c2ecf20Sopenharmony_ci * -EEXIST if there is already an entry with the same key,
588c2ecf20Sopenharmony_ci * -EINVAL for general errors or
598c2ecf20Sopenharmony_ci  0 otherwise.
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_cistatic inline int hashtab_insert(struct hashtab *h, void *key, void *datum,
628c2ecf20Sopenharmony_ci				 struct hashtab_key_params key_params)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	u32 hvalue;
658c2ecf20Sopenharmony_ci	struct hashtab_node *prev, *cur;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	cond_resched();
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (!h->size || h->nel == HASHTAB_MAX_NODES)
708c2ecf20Sopenharmony_ci		return -EINVAL;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	hvalue = key_params.hash(key) & (h->size - 1);
738c2ecf20Sopenharmony_ci	prev = NULL;
748c2ecf20Sopenharmony_ci	cur = h->htable[hvalue];
758c2ecf20Sopenharmony_ci	while (cur) {
768c2ecf20Sopenharmony_ci		int cmp = key_params.cmp(key, cur->key);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci		if (cmp == 0)
798c2ecf20Sopenharmony_ci			return -EEXIST;
808c2ecf20Sopenharmony_ci		if (cmp < 0)
818c2ecf20Sopenharmony_ci			break;
828c2ecf20Sopenharmony_ci		prev = cur;
838c2ecf20Sopenharmony_ci		cur = cur->next;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return __hashtab_insert(h, prev ? &prev->next : &h->htable[hvalue],
878c2ecf20Sopenharmony_ci				key, datum);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/*
918c2ecf20Sopenharmony_ci * Searches for the entry with the specified key in the hash table.
928c2ecf20Sopenharmony_ci *
938c2ecf20Sopenharmony_ci * Returns NULL if no entry has the specified key or
948c2ecf20Sopenharmony_ci * the datum of the entry otherwise.
958c2ecf20Sopenharmony_ci */
968c2ecf20Sopenharmony_cistatic inline void *hashtab_search(struct hashtab *h, const void *key,
978c2ecf20Sopenharmony_ci				   struct hashtab_key_params key_params)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	u32 hvalue;
1008c2ecf20Sopenharmony_ci	struct hashtab_node *cur;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (!h->size)
1038c2ecf20Sopenharmony_ci		return NULL;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	hvalue = key_params.hash(key) & (h->size - 1);
1068c2ecf20Sopenharmony_ci	cur = h->htable[hvalue];
1078c2ecf20Sopenharmony_ci	while (cur) {
1088c2ecf20Sopenharmony_ci		int cmp = key_params.cmp(key, cur->key);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		if (cmp == 0)
1118c2ecf20Sopenharmony_ci			return cur->datum;
1128c2ecf20Sopenharmony_ci		if (cmp < 0)
1138c2ecf20Sopenharmony_ci			break;
1148c2ecf20Sopenharmony_ci		cur = cur->next;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci	return NULL;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/*
1208c2ecf20Sopenharmony_ci * Destroys the specified hash table.
1218c2ecf20Sopenharmony_ci */
1228c2ecf20Sopenharmony_civoid hashtab_destroy(struct hashtab *h);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/*
1258c2ecf20Sopenharmony_ci * Applies the specified apply function to (key,datum,args)
1268c2ecf20Sopenharmony_ci * for each entry in the specified hash table.
1278c2ecf20Sopenharmony_ci *
1288c2ecf20Sopenharmony_ci * The order in which the function is applied to the entries
1298c2ecf20Sopenharmony_ci * is dependent upon the internal structure of the hash table.
1308c2ecf20Sopenharmony_ci *
1318c2ecf20Sopenharmony_ci * If apply returns a non-zero status, then hashtab_map will cease
1328c2ecf20Sopenharmony_ci * iterating through the hash table and will propagate the error
1338c2ecf20Sopenharmony_ci * return to its caller.
1348c2ecf20Sopenharmony_ci */
1358c2ecf20Sopenharmony_ciint hashtab_map(struct hashtab *h,
1368c2ecf20Sopenharmony_ci		int (*apply)(void *k, void *d, void *args),
1378c2ecf20Sopenharmony_ci		void *args);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ciint hashtab_duplicate(struct hashtab *new, struct hashtab *orig,
1408c2ecf20Sopenharmony_ci		int (*copy)(struct hashtab_node *new,
1418c2ecf20Sopenharmony_ci			struct hashtab_node *orig, void *args),
1428c2ecf20Sopenharmony_ci		int (*destroy)(void *k, void *d, void *args),
1438c2ecf20Sopenharmony_ci		void *args);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/* Fill info with some hash table statistics */
1468c2ecf20Sopenharmony_civoid hashtab_stat(struct hashtab *h, struct hashtab_info *info);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci#endif	/* _SS_HASHTAB_H */
149