1lhash(3) OpenSSL lhash(3)
2
3
4
6 lh_new, lh_free, lh_insert, lh_delete, lh_retrieve, lh_doall,
7 lh_doall_arg, lh_error - dynamic hash table
8
10 #include <openssl/lhash.h>
11
12 LHASH *lh_new(LHASH_HASH_FN_TYPE hash, LHASH_COMP_FN_TYPE compare);
13 void lh_free(LHASH *table);
14
15 void *lh_insert(LHASH *table, void *data);
16 void *lh_delete(LHASH *table, void *data);
17 void *lh_retrieve(LHASH *table, void *data);
18
19 void lh_doall(LHASH *table, LHASH_DOALL_FN_TYPE func);
20 void lh_doall_arg(LHASH *table, LHASH_DOALL_ARG_FN_TYPE func,
21 void *arg);
22
23 int lh_error(LHASH *table);
24
25 typedef int (*LHASH_COMP_FN_TYPE)(const void *, const void *);
26 typedef unsigned long (*LHASH_HASH_FN_TYPE)(const void *);
27 typedef void (*LHASH_DOALL_FN_TYPE)(const void *);
28 typedef void (*LHASH_DOALL_ARG_FN_TYPE)(const void *, const void *);
29
31 This library implements dynamic hash tables. The hash table entries can
32 be arbitrary structures. Usually they consist of key and value fields.
33
34 lh_new() creates a new LHASH structure to store arbitrary data entries,
35 and provides the 'hash' and 'compare' callbacks to be used in organis‐
36 ing the table's entries. The hash callback takes a pointer to a table
37 entry as its argument and returns an unsigned long hash value for its
38 key field. The hash value is normally truncated to a power of 2, so
39 make sure that your hash function returns well mixed low order bits.
40 The compare callback takes two arguments (pointers to two hash table
41 entries), and returns 0 if their keys are equal, non-zero otherwise.
42 If your hash table will contain items of some particular type and the
43 hash and compare callbacks hash/compare these types, then the
44 DECLARE_LHASH_HASH_FN and IMPLEMENT_LHASH_COMP_FN macros can be used to
45 create callback wrappers of the prototypes required by lh_new(). These
46 provide per-variable casts before calling the type-specific callbacks
47 written by the application author. These macros, as well as those used
48 for the "doall" callbacks, are defined as;
49
50 #define DECLARE_LHASH_HASH_FN(f_name,o_type) \
51 unsigned long f_name##_LHASH_HASH(const void *);
52 #define IMPLEMENT_LHASH_HASH_FN(f_name,o_type) \
53 unsigned long f_name##_LHASH_HASH(const void *arg) { \
54 o_type a = (o_type)arg; \
55 return f_name(a); }
56 #define LHASH_HASH_FN(f_name) f_name##_LHASH_HASH
57
58 #define DECLARE_LHASH_COMP_FN(f_name,o_type) \
59 int f_name##_LHASH_COMP(const void *, const void *);
60 #define IMPLEMENT_LHASH_COMP_FN(f_name,o_type) \
61 int f_name##_LHASH_COMP(const void *arg1, const void *arg2) { \
62 o_type a = (o_type)arg1; \
63 o_type b = (o_type)arg2; \
64 return f_name(a,b); }
65 #define LHASH_COMP_FN(f_name) f_name##_LHASH_COMP
66
67 #define DECLARE_LHASH_DOALL_FN(f_name,o_type) \
68 void f_name##_LHASH_DOALL(const void *);
69 #define IMPLEMENT_LHASH_DOALL_FN(f_name,o_type) \
70 void f_name##_LHASH_DOALL(const void *arg) { \
71 o_type a = (o_type)arg; \
72 f_name(a); }
73 #define LHASH_DOALL_FN(f_name) f_name##_LHASH_DOALL
74
75 #define DECLARE_LHASH_DOALL_ARG_FN(f_name,o_type,a_type) \
76 void f_name##_LHASH_DOALL_ARG(const void *, const void *);
77 #define IMPLEMENT_LHASH_DOALL_ARG_FN(f_name,o_type,a_type) \
78 void f_name##_LHASH_DOALL_ARG(const void *arg1, const void *arg2) { \
79 o_type a = (o_type)arg1; \
80 a_type b = (a_type)arg2; \
81 f_name(a,b); }
82 #define LHASH_DOALL_ARG_FN(f_name) f_name##_LHASH_DOALL_ARG
83
84 An example of a hash table storing (pointers to) structures of type
85 'STUFF' could be defined as follows;
86
87 /* Calculates the hash value of 'tohash' (implemented elsewhere) */
88 unsigned long STUFF_hash(const STUFF *tohash);
89 /* Orders 'arg1' and 'arg2' (implemented elsewhere) */
90 int STUFF_cmp(const STUFF *arg1, const STUFF *arg2);
91 /* Create the type-safe wrapper functions for use in the LHASH internals */
92 static IMPLEMENT_LHASH_HASH_FN(STUFF_hash, const STUFF *)
93 static IMPLEMENT_LHASH_COMP_FN(STUFF_cmp, const STUFF *);
94 /* ... */
95 int main(int argc, char *argv[]) {
96 /* Create the new hash table using the hash/compare wrappers */
97 LHASH *hashtable = lh_new(LHASH_HASH_FN(STUFF_hash),
98 LHASH_COMP_FN(STUFF_cmp));
99 /* ... */
100 }
101
102 lh_free() frees the LHASH structure table. Allocated hash table entries
103 will not be freed; consider using lh_doall() to deallocate any remain‐
104 ing entries in the hash table (see below).
105
106 lh_insert() inserts the structure pointed to by data into table. If
107 there already is an entry with the same key, the old value is replaced.
108 Note that lh_insert() stores pointers, the data are not copied.
109
110 lh_delete() deletes an entry from table.
111
112 lh_retrieve() looks up an entry in table. Normally, data is a structure
113 with the key field(s) set; the function will return a pointer to a
114 fully populated structure.
115
116 lh_doall() will, for every entry in the hash table, call func with the
117 data item as its parameter. For lh_doall() and lh_doall_arg(), func‐
118 tion pointer casting should be avoided in the callbacks (see NOTE) -
119 instead, either declare the callbacks to match the prototype required
120 in lh_new() or use the declare/implement macros to create type-safe
121 wrappers that cast variables prior to calling your type-specific call‐
122 backs. An example of this is illustrated here where the callback is
123 used to cleanup resources for items in the hash table prior to the
124 hashtable itself being deallocated:
125
126 /* Cleans up resources belonging to 'a' (this is implemented elsewhere) */
127 void STUFF_cleanup(STUFF *a);
128 /* Implement a prototype-compatible wrapper for "STUFF_cleanup" */
129 IMPLEMENT_LHASH_DOALL_FN(STUFF_cleanup, STUFF *)
130 /* ... then later in the code ... */
131 /* So to run "STUFF_cleanup" against all items in a hash table ... */
132 lh_doall(hashtable, LHASH_DOALL_FN(STUFF_cleanup));
133 /* Then the hash table itself can be deallocated */
134 lh_free(hashtable);
135
136 When doing this, be careful if you delete entries from the hash table
137 in your callbacks: the table may decrease in size, moving the item that
138 you are currently on down lower in the hash table - this could cause
139 some entries to be skipped during the iteration. The second best solu‐
140 tion to this problem is to set hash->down_load=0 before you start
141 (which will stop the hash table ever decreasing in size). The best
142 solution is probably to avoid deleting items from the hash table inside
143 a "doall" callback!
144
145 lh_doall_arg() is the same as lh_doall() except that func will be
146 called with arg as the second argument and func should be of type
147 LHASH_DOALL_ARG_FN_TYPE (a callback prototype that is passed both the
148 table entry and an extra argument). As with lh_doall(), you can
149 instead choose to declare your callback with a prototype matching the
150 types you are dealing with and use the declare/implement macros to cre‐
151 ate compatible wrappers that cast variables before calling your type-
152 specific callbacks. An example of this is demonstrated here (printing
153 all hash table entries to a BIO that is provided by the caller):
154
155 /* Prints item 'a' to 'output_bio' (this is implemented elsewhere) */
156 void STUFF_print(const STUFF *a, BIO *output_bio);
157 /* Implement a prototype-compatible wrapper for "STUFF_print" */
158 static IMPLEMENT_LHASH_DOALL_ARG_FN(STUFF_print, const STUFF *, BIO *)
159 /* ... then later in the code ... */
160 /* Print out the entire hashtable to a particular BIO */
161 lh_doall_arg(hashtable, LHASH_DOALL_ARG_FN(STUFF_print), logging_bio);
162
163 lh_error() can be used to determine if an error occurred in the last
164 operation. lh_error() is a macro.
165
167 lh_new() returns NULL on error, otherwise a pointer to the new LHASH
168 structure.
169
170 When a hash table entry is replaced, lh_insert() returns the value
171 being replaced. NULL is returned on normal operation and on error.
172
173 lh_delete() returns the entry being deleted. NULL is returned if there
174 is no such value in the hash table.
175
176 lh_retrieve() returns the hash table entry if it has been found, NULL
177 otherwise.
178
179 lh_error() returns 1 if an error occurred in the last operation, 0 oth‐
180 erwise.
181
182 lh_free(), lh_doall() and lh_doall_arg() return no values.
183
185 The various LHASH macros and callback types exist to make it possible
186 to write type-safe code without resorting to function-prototype casting
187 - an evil that makes application code much harder to audit/verify and
188 also opens the window of opportunity for stack corruption and other
189 hard-to-find bugs. It also, apparently, violates ANSI-C.
190
191 The LHASH code regards table entries as constant data. As such, it
192 internally represents lh_insert()'d items with a "const void *" pointer
193 type. This is why callbacks such as those used by lh_doall() and
194 lh_doall_arg() declare their prototypes with "const", even for the
195 parameters that pass back the table items' data pointers - for consis‐
196 tency, user-provided data is "const" at all times as far as the LHASH
197 code is concerned. However, as callers are themselves providing these
198 pointers, they can choose whether they too should be treating all such
199 parameters as constant.
200
201 As an example, a hash table may be maintained by code that, for reasons
202 of encapsulation, has only "const" access to the data being indexed in
203 the hash table (ie. it is returned as "const" from elsewhere in their
204 code) - in this case the LHASH prototypes are appropriate as-is. Con‐
205 versely, if the caller is responsible for the life-time of the data in
206 question, then they may well wish to make modifications to table item
207 passed back in the lh_doall() or lh_doall_arg() callbacks (see the
208 "STUFF_cleanup" example above). If so, the caller can either cast the
209 "const" away (if they're providing the raw callbacks themselves) or use
210 the macros to declare/implement the wrapper functions without "const"
211 types.
212
213 Callers that only have "const" access to data they're indexing in a ta‐
214 ble, yet declare callbacks without constant types (or cast the "const"
215 away themselves), are therefore creating their own risks/bugs without
216 being encouraged to do so by the API. On a related note, those audit‐
217 ing code should pay special attention to any instances of
218 DECLARE/IMPLEMENT_LHASH_DOALL_[ARG_]_FN macros that provide types with‐
219 out any "const" qualifiers.
220
222 lh_insert() returns NULL both for success and error.
223
225 The following description is based on the SSLeay documentation:
226
227 The lhash library implements a hash table described in the Communica‐
228 tions of the ACM in 1991. What makes this hash table different is that
229 as the table fills, the hash table is increased (or decreased) in size
230 via OPENSSL_realloc(). When a 'resize' is done, instead of all hashes
231 being redistributed over twice as many 'buckets', one bucket is split.
232 So when an 'expand' is done, there is only a minimal cost to redis‐
233 tribute some values. Subsequent inserts will cause more single
234 'bucket' redistributions but there will never be a sudden large cost
235 due to redistributing all the 'buckets'.
236
237 The state for a particular hash table is kept in the LHASH structure.
238 The decision to increase or decrease the hash table size is made
239 depending on the 'load' of the hash table. The load is the number of
240 items in the hash table divided by the size of the hash table. The
241 default values are as follows. If (hash->up_load < load) => expand.
242 if (hash->down_load > load) => contract. The up_load has a default
243 value of 1 and down_load has a default value of 2. These numbers can
244 be modified by the application by just playing with the up_load and
245 down_load variables. The 'load' is kept in a form which is multiplied
246 by 256. So hash->up_load=8*256; will cause a load of 8 to be set.
247
248 If you are interested in performance the field to watch is
249 num_comp_calls. The hash library keeps track of the 'hash' value for
250 each item so when a lookup is done, the 'hashes' are compared, if there
251 is a match, then a full compare is done, and hash->num_comp_calls is
252 incremented. If num_comp_calls is not equal to num_delete plus
253 num_retrieve it means that your hash function is generating hashes that
254 are the same for different values. It is probably worth changing your
255 hash function if this is the case because even if your hash table has
256 10 items in a 'bucket', it can be searched with 10 unsigned long com‐
257 pares and 10 linked list traverses. This will be much less expensive
258 that 10 calls to your compare function.
259
260 lh_strhash() is a demo string hashing function:
261
262 unsigned long lh_strhash(const char *c);
263
264 Since the LHASH routines would normally be passed structures, this rou‐
265 tine would not normally be passed to lh_new(), rather it would be used
266 in the function passed to lh_new().
267
269 lh_stats(3)
270
272 The lhash library is available in all versions of SSLeay and OpenSSL.
273 lh_error() was added in SSLeay 0.9.1b.
274
275 This manpage is derived from the SSLeay documentation.
276
277 In OpenSSL 0.9.7, all lhash functions that were passed function point‐
278 ers were changed for better type safety, and the function types
279 LHASH_COMP_FN_TYPE, LHASH_HASH_FN_TYPE, LHASH_DOALL_FN_TYPE and
280 LHASH_DOALL_ARG_FN_TYPE became available.
281
282
283
2840.9.8b 2002-07-18 lhash(3)