xref: /freebsd/usr.sbin/nscd/hashtable.h (revision 28f805ce)
1 /*-
2  * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #ifndef __CACHELIB_HASHTABLE_H__
30 #define __CACHELIB_HASHTABLE_H__
31 
32 #include <string.h>
33 
34 #define HASHTABLE_INITIAL_ENTRIES_CAPACITY 8
35 typedef int hashtable_index_t;
36 
37 /*
38  * This file contains queue.h-like macro definitions for hash tables.
39  * Hash table is organized as an array of the specified size of the user
40  * defined (with HASTABLE_ENTRY_HEAD) structures. Each hash table
41  * entry (user defined structure) stores its elements in the sorted array.
42  * You can place elements into the hash table, retrieve elements with
43  * specified key, traverse through all elements, and delete them.
44  * New elements are placed into the hash table by using the compare and
45  * hashing functions, provided by the user.
46  */
47 
48 /*
49  * Defines the hash table entry structure, that uses specified type of
50  * elements.
51  */
52 #define HASHTABLE_ENTRY_HEAD(name, type) struct name {			\
53 	type	*values;						\
54 	size_t capacity;						\
55 	size_t size;							\
56 }
57 
58 /*
59  * Defines the hash table structure, which uses the specified type of entries.
60  * The only restriction for entries is that is that they should have the field,
61  * defined with HASHTABLE_ENTRY_HEAD macro.
62  */
63 #define HASHTABLE_HEAD(name, entry) struct name {			\
64 	struct entry	*entries;					\
65 	size_t		entries_size;					\
66 }
67 
68 #define HASHTABLE_ENTRIES_COUNT(table) ((table)->entries_size)
69 
70 /*
71  * Unlike most of queue.h data types, hash tables can not be initialized
72  * statically - so there is no HASHTABLE_HEAD_INITIALIZED macro.
73  */
74 #define HASHTABLE_INIT(table, type, field, _entries_size)		\
75 	do {								\
76 		hashtable_index_t var;					\
77 		(table)->entries = (void *)calloc(1,			\
78 			sizeof(*(table)->entries) * (_entries_size));	\
79 		(table)->entries_size = (_entries_size);		\
80 		for (var = 0; var < HASHTABLE_ENTRIES_COUNT(table); ++var) {\
81 			(table)->entries[var].field.capacity = 		\
82 				HASHTABLE_INITIAL_ENTRIES_CAPACITY;	\
83 			(table)->entries[var].field.size = 0;		\
84 			(table)->entries[var].field.values = (type *)malloc(\
85 				sizeof(type) * 				\
86 		    		HASHTABLE_INITIAL_ENTRIES_CAPACITY);	\
87 			assert((table)->entries[var].field.values != NULL);\
88 		}							\
89 	} while (0)
90 
91 /*
92  * All initialized hashtables should be destroyed with this macro.
93  */
94 #define HASHTABLE_DESTROY(table, field)					\
95 	do {								\
96 		hashtable_index_t var;					\
97 		for (var = 0; var < HASHTABLE_ENTRIES_COUNT(table); ++var) {\
98 			free((table)->entries[var].field.values);	\
99 		}							\
100 	} while (0)
101 
102 #define HASHTABLE_GET_ENTRY(table, hash)	(&((table)->entries[hash]))
103 
104 /*
105  * Traverses through all hash table entries
106  */
107 #define HASHTABLE_FOREACH(table, var)					\
108 	for ((var) = &((table)->entries[0]);				\
109 		(var) < &((table)->entries[HASHTABLE_ENTRIES_COUNT(table)]);\
110 		++(var))
111 
112 /*
113  * Traverses through all elements of the specified hash table entry
114  */
115 #define HASHTABLE_ENTRY_FOREACH(entry, field, var)			\
116 	for ((var) = &((entry)->field.values[0]);			\
117 		(var) < &((entry)->field.values[(entry)->field.size]);	\
118 		++(var))
119 
120 #define HASHTABLE_ENTRY_CLEAR(entry, field)				\
121 	((entry)->field.size = 0)
122 
123 #define HASHTABLE_ENTRY_SIZE(entry, field)				\
124 	((entry)->field.size)
125 
126 #define HASHTABLE_ENTRY_CAPACITY(entry, field)				\
127 	((entry)->field.capacity)
128 
129 #define HASHTABLE_ENTRY_CAPACITY_INCREASE(entry, field, type)		\
130 	(entry)->field.capacity *= 2;					\
131 	(entry)->field.values = (type *)realloc((entry)->field.values, 	\
132 		(entry)->field.capacity * sizeof(type));
133 
134 #define HASHTABLE_ENTRY_CAPACITY_DECREASE(entry, field, type)		\
135 	(entry)->field.capacity /= 2;					\
136 	(entry)->field.values = (type *)realloc((entry)->field.values, 	\
137 		(entry)->field.capacity * sizeof(type));
138 
139 /*
140  * Generates prototypes for the hash table functions
141  */
142 #define HASHTABLE_PROTOTYPE(name, entry_, type)				\
143 hashtable_index_t name##_CALCULATE_HASH(struct name *, type *);		\
144 void name##_ENTRY_STORE(struct entry_*, type *);			\
145 type *name##_ENTRY_FIND(struct entry_*, type *);			\
146 type *name##_ENTRY_FIND_SPECIAL(struct entry_ *, type *,		\
147 	int (*) (const void *, const void *));				\
148 void name##_ENTRY_REMOVE(struct entry_*, type *);
149 
150 /*
151  * Generates implementations of the hash table functions
152  */
153 #define HASHTABLE_GENERATE(name, entry_, type, field, HASH, CMP)	\
154 hashtable_index_t name##_CALCULATE_HASH(struct name *table, type *data)	\
155 {									\
156 									\
157 	return HASH(data, table->entries_size);				\
158 }									\
159 									\
160 void name##_ENTRY_STORE(struct entry_ *the_entry, type *data)		\
161 {									\
162 									\
163 	if (the_entry->field.size == the_entry->field.capacity)		\
164 		HASHTABLE_ENTRY_CAPACITY_INCREASE(the_entry, field, type);\
165 									\
166 	memcpy(&(the_entry->field.values[the_entry->field.size++]),	\
167 		data,							\
168 		sizeof(type));						\
169 	qsort(the_entry->field.values, the_entry->field.size, 		\
170 		sizeof(type), CMP);					\
171 }									\
172 									\
173 type *name##_ENTRY_FIND(struct entry_ *the_entry, type *key)		\
174 {									\
175 									\
176 	return ((type *)bsearch(key, the_entry->field.values,	 	\
177 		the_entry->field.size, sizeof(type), CMP));		\
178 }									\
179 									\
180 type *name##_ENTRY_FIND_SPECIAL(struct entry_ *the_entry, type *key,	\
181 	int (*compar) (const void *, const void *))			\
182 {									\
183 	return ((type *)bsearch(key, the_entry->field.values,	 	\
184 		the_entry->field.size, sizeof(type), compar));		\
185 }									\
186 									\
187 void name##_ENTRY_REMOVE(struct entry_ *the_entry, type *del_elm)	\
188 {									\
189 									\
190 	memmove(del_elm, del_elm + 1, 					\
191 		(&the_entry->field.values[--the_entry->field.size] - del_elm) *\
192 		sizeof(type));						\
193 }
194 
195 /*
196  * Macro definitions below wrap the functions, generaed with
197  * HASHTABLE_GENERATE macro. You should use them and avoid using generated
198  * functions directly.
199  */
200 #define HASHTABLE_CALCULATE_HASH(name, table, data)			\
201 	(name##_CALCULATE_HASH((table), data))
202 
203 #define HASHTABLE_ENTRY_STORE(name, entry, data)			\
204 	name##_ENTRY_STORE((entry), data)
205 
206 #define HASHTABLE_ENTRY_FIND(name, entry, key)				\
207 	(name##_ENTRY_FIND((entry), (key)))
208 
209 #define HASHTABLE_ENTRY_FIND_SPECIAL(name, entry, key, cmp)		\
210 	(name##_ENTRY_FIND_SPECIAL((entry), (key), (cmp)))
211 
212 #define HASHTABLE_ENTRY_REMOVE(name, entry, del_elm)			\
213 	name##_ENTRY_REMOVE((entry), (del_elm))
214 
215 #endif
216