xref: /titanic_44/usr/src/lib/smbsrv/libsmb/common/smb_ht.c (revision da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0)
1*da6c28aaSamw /*
2*da6c28aaSamw  * CDDL HEADER START
3*da6c28aaSamw  *
4*da6c28aaSamw  * The contents of this file are subject to the terms of the
5*da6c28aaSamw  * Common Development and Distribution License (the "License").
6*da6c28aaSamw  * You may not use this file except in compliance with the License.
7*da6c28aaSamw  *
8*da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10*da6c28aaSamw  * See the License for the specific language governing permissions
11*da6c28aaSamw  * and limitations under the License.
12*da6c28aaSamw  *
13*da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14*da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16*da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17*da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18*da6c28aaSamw  *
19*da6c28aaSamw  * CDDL HEADER END
20*da6c28aaSamw  */
21*da6c28aaSamw /*
22*da6c28aaSamw  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*da6c28aaSamw  * Use is subject to license terms.
24*da6c28aaSamw  */
25*da6c28aaSamw 
26*da6c28aaSamw #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*da6c28aaSamw 
28*da6c28aaSamw /*
29*da6c28aaSamw  * Generic hash table library. The hash table is an array of pointers
30*da6c28aaSamw  * to items. Hash collisions are handled using linked lists from the
31*da6c28aaSamw  * table entries. A handle is associated with each table, which is used
32*da6c28aaSamw  * to maintain the hash table.
33*da6c28aaSamw  *
34*da6c28aaSamw  * +------+     +-------+    +----+    +----+
35*da6c28aaSamw  * |handle|---> |index 0|--->|item|--->|item|--->
36*da6c28aaSamw  * | ...  |     +-------+    +----+    +----+
37*da6c28aaSamw  * | ...  |     |index 1|--->
38*da6c28aaSamw  * +------+     +-------+    +----+    +----+    +----+
39*da6c28aaSamw  *              |index 2|--->|item|--->|item|--->|item|--->
40*da6c28aaSamw  *              +-------+    +----+    +----+    +----+
41*da6c28aaSamw  *              | ...   |--->
42*da6c28aaSamw  *              +-------+
43*da6c28aaSamw  *              | ...   |--->
44*da6c28aaSamw  *              +-------+
45*da6c28aaSamw  *              |index n|--->
46*da6c28aaSamw  *              +-------+
47*da6c28aaSamw  *
48*da6c28aaSamw  */
49*da6c28aaSamw 
50*da6c28aaSamw #include <stdlib.h>
51*da6c28aaSamw #include <strings.h>
52*da6c28aaSamw #include <smbsrv/hash_table.h>
53*da6c28aaSamw 
54*da6c28aaSamw static size_t ht_default_hash(HT_HANDLE *handle, const char *key);
55*da6c28aaSamw 
56*da6c28aaSamw /*
57*da6c28aaSamw  * ht_is_power2
58*da6c28aaSamw  *
59*da6c28aaSamw  * Inline function to determine if a value is a power of two. This
60*da6c28aaSamw  * function is used by the library to validate the table size when
61*da6c28aaSamw  * a new table is created.
62*da6c28aaSamw  *
63*da6c28aaSamw  * Returns 1 if value given is power of two, otherwise returns 0.
64*da6c28aaSamw  */
65*da6c28aaSamw static size_t
ht_is_power2(size_t value)66*da6c28aaSamw ht_is_power2(size_t value)
67*da6c28aaSamw {
68*da6c28aaSamw 	return (((value & (value - 1)) == 0)? 1 : 0);
69*da6c28aaSamw }
70*da6c28aaSamw 
71*da6c28aaSamw 
72*da6c28aaSamw /*
73*da6c28aaSamw  * ht_create_table
74*da6c28aaSamw  *
75*da6c28aaSamw  * Create a hash table. The table size must be a positive integer and
76*da6c28aaSamw  * must be a power of two. The key size must be a positive integer.
77*da6c28aaSamw  * For null terminated keys, the key size does not need to include the
78*da6c28aaSamw  * null terminating character. The type of key is indicated by the
79*da6c28aaSamw  * flags (see hash_table.h).
80*da6c28aaSamw  *
81*da6c28aaSamw  * The handle and the table are are malloc'd using a single call, to
82*da6c28aaSamw  * avoid two allocations. The table is located immediately after the
83*da6c28aaSamw  * handle.
84*da6c28aaSamw  *
85*da6c28aaSamw  * On success a pointer to an opaque handle is returned. Otherwise a
86*da6c28aaSamw  * null pointer is returned.
87*da6c28aaSamw  */
88*da6c28aaSamw HT_HANDLE *
ht_create_table(size_t table_size,size_t key_size,size_t flags)89*da6c28aaSamw ht_create_table(size_t table_size, size_t key_size, size_t flags)
90*da6c28aaSamw {
91*da6c28aaSamw 	HT_HANDLE *ht;
92*da6c28aaSamw 	size_t msize;
93*da6c28aaSamw 	size_t i;
94*da6c28aaSamw 
95*da6c28aaSamw 	if ((table_size == 0) || (key_size == 0))
96*da6c28aaSamw 		return (NULL);
97*da6c28aaSamw 
98*da6c28aaSamw 	if (ht_is_power2(table_size) == 0)
99*da6c28aaSamw 		return (NULL);
100*da6c28aaSamw 
101*da6c28aaSamw 	msize = sizeof (HT_HANDLE) + (sizeof (HT_TABLE_ENTRY) * table_size);
102*da6c28aaSamw 
103*da6c28aaSamw 	if ((ht = (HT_HANDLE *)malloc(msize)) == 0)
104*da6c28aaSamw 		return (NULL);
105*da6c28aaSamw 
106*da6c28aaSamw 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
107*da6c28aaSamw 	ht->ht_table = (HT_TABLE_ENTRY *)((char *)ht + sizeof (HT_HANDLE));
108*da6c28aaSamw 	ht->ht_table_size = table_size;
109*da6c28aaSamw 	ht->ht_table_mask = table_size - 1;
110*da6c28aaSamw 	ht->ht_key_size = key_size;
111*da6c28aaSamw 	ht->ht_total_items = 0;
112*da6c28aaSamw 	ht->ht_flags = flags;
113*da6c28aaSamw 	ht->ht_hash = ht_default_hash;
114*da6c28aaSamw 	ht->ht_callback = 0;
115*da6c28aaSamw 	ht->ht_sequence = random();
116*da6c28aaSamw 	ht->ht_cmp = ((flags & HTHF_FIXED_KEY) == 0)
117*da6c28aaSamw 	    ? (HT_CMP)strncmp : (HT_CMP)memcmp;
118*da6c28aaSamw 
119*da6c28aaSamw 	for (i = 0; i < table_size; i++)
120*da6c28aaSamw 		bzero(&ht->ht_table[i], sizeof (HT_TABLE_ENTRY));
121*da6c28aaSamw 
122*da6c28aaSamw 	return (ht);
123*da6c28aaSamw }
124*da6c28aaSamw 
125*da6c28aaSamw 
126*da6c28aaSamw /*
127*da6c28aaSamw  * ht_destroy_table
128*da6c28aaSamw  *
129*da6c28aaSamw  * Destroy a hash table. All entries in the table are removed, which
130*da6c28aaSamw  * may invoke the callback if it's installed, and the memory is freed.
131*da6c28aaSamw  */
132*da6c28aaSamw void
ht_destroy_table(HT_HANDLE * handle)133*da6c28aaSamw ht_destroy_table(HT_HANDLE *handle)
134*da6c28aaSamw {
135*da6c28aaSamw 	HT_ITEM *item;
136*da6c28aaSamw 	HT_ITERATOR iterator;
137*da6c28aaSamw 
138*da6c28aaSamw 	if (handle == 0)
139*da6c28aaSamw 		return;
140*da6c28aaSamw 
141*da6c28aaSamw 	/* To remove marked entries */
142*da6c28aaSamw 	(void) ht_clean_table(handle);
143*da6c28aaSamw 	while ((item = ht_findfirst(handle, &iterator)) != 0)
144*da6c28aaSamw 		(void) ht_remove_item(handle, item->hi_key);
145*da6c28aaSamw 
146*da6c28aaSamw 	free(handle);
147*da6c28aaSamw }
148*da6c28aaSamw 
149*da6c28aaSamw 
150*da6c28aaSamw /*
151*da6c28aaSamw  * ht_get_total_items
152*da6c28aaSamw  *
153*da6c28aaSamw  * Return the total number of items in the table. Returns -1 if the
154*da6c28aaSamw  * handle is invalid.
155*da6c28aaSamw  */
156*da6c28aaSamw size_t
ht_get_total_items(HT_HANDLE * handle)157*da6c28aaSamw ht_get_total_items(HT_HANDLE *handle)
158*da6c28aaSamw {
159*da6c28aaSamw 	if (handle == 0)
160*da6c28aaSamw 		return ((size_t)-1);
161*da6c28aaSamw 
162*da6c28aaSamw 	return (handle->ht_total_items);
163*da6c28aaSamw }
164*da6c28aaSamw 
165*da6c28aaSamw 
166*da6c28aaSamw /*
167*da6c28aaSamw  * ht_default_hash
168*da6c28aaSamw  *
169*da6c28aaSamw  * Default hash function to compute the table index (hash value) based
170*da6c28aaSamw  * on the specified key. This will identify the location for the
171*da6c28aaSamw  * corresponding item in the hash table. The handle and key pointers
172*da6c28aaSamw  * should be validated before this function is called.
173*da6c28aaSamw  *
174*da6c28aaSamw  * Returns the table index location for the item.
175*da6c28aaSamw  */
176*da6c28aaSamw static size_t
ht_default_hash(HT_HANDLE * handle,const char * key)177*da6c28aaSamw ht_default_hash(HT_HANDLE *handle, const char *key)
178*da6c28aaSamw {
179*da6c28aaSamw 	unsigned int hash_ndx = 0;
180*da6c28aaSamw 	size_t rval;
181*da6c28aaSamw 
182*da6c28aaSamw 	if ((handle->ht_flags & HTHF_FIXED_KEY) == 0) {
183*da6c28aaSamw 		while (*key) {
184*da6c28aaSamw 			hash_ndx += *key;
185*da6c28aaSamw 			++key;
186*da6c28aaSamw 		}
187*da6c28aaSamw 	} else {
188*da6c28aaSamw 		int key_len = handle->ht_key_size;
189*da6c28aaSamw 
190*da6c28aaSamw 		while (key_len--) {
191*da6c28aaSamw 			hash_ndx += *key;
192*da6c28aaSamw 			++key;
193*da6c28aaSamw 		}
194*da6c28aaSamw 	}
195*da6c28aaSamw 
196*da6c28aaSamw 	rval = (hash_ndx * HASH_MESH_VALUE) & handle->ht_table_mask;
197*da6c28aaSamw 	return (rval);
198*da6c28aaSamw }
199*da6c28aaSamw 
200*da6c28aaSamw 
201*da6c28aaSamw /*
202*da6c28aaSamw  * ht_set_cmpfn
203*da6c28aaSamw  *
204*da6c28aaSamw  * Replace the current compare function. As the this is function
205*da6c28aaSamw  * for comparing items' key, it should not be called while there are
206*da6c28aaSamw  * items in the table.
207*da6c28aaSamw  */
208*da6c28aaSamw void
ht_set_cmpfn(HT_HANDLE * handle,HT_CMP cmpfn)209*da6c28aaSamw ht_set_cmpfn(HT_HANDLE *handle, HT_CMP cmpfn)
210*da6c28aaSamw {
211*da6c28aaSamw 	if (handle)
212*da6c28aaSamw 		handle->ht_cmp = cmpfn;
213*da6c28aaSamw }
214*da6c28aaSamw 
215*da6c28aaSamw /*
216*da6c28aaSamw  * ht_add_item
217*da6c28aaSamw  *
218*da6c28aaSamw  * Adds an item to a hash table. The hash table is identified by the
219*da6c28aaSamw  * handle and the key is used to generate a hashed index. The data
220*da6c28aaSamw  * item can be null; it is never dereferenced. We don't check for
221*da6c28aaSamw  * duplicates. If duplicate keys are added to the table, the last
222*da6c28aaSamw  * item added will be to the front of the duplicate list.
223*da6c28aaSamw  *
224*da6c28aaSamw  * The table sequence number may be modified here.
225*da6c28aaSamw  *
226*da6c28aaSamw  * If the item is successfully inserted, a pointer to the item object
227*da6c28aaSamw  * is returned. Otherwise a null pointer is returned.
228*da6c28aaSamw  */
229*da6c28aaSamw HT_ITEM *
ht_add_item(HT_HANDLE * handle,const char * key,const void * data)230*da6c28aaSamw ht_add_item(HT_HANDLE *handle, const char *key, const void *data)
231*da6c28aaSamw {
232*da6c28aaSamw 	size_t h_index, key_len;
233*da6c28aaSamw 	size_t msize;
234*da6c28aaSamw 	HT_ITEM *item;
235*da6c28aaSamw 
236*da6c28aaSamw 	if (handle == 0 || key == 0)
237*da6c28aaSamw 		return (NULL);
238*da6c28aaSamw 
239*da6c28aaSamw 	if (handle->ht_flags & HTHF_FIXED_KEY) {
240*da6c28aaSamw 		key_len = handle->ht_key_size;
241*da6c28aaSamw 	} else {
242*da6c28aaSamw 		key_len = strlen(key);
243*da6c28aaSamw 
244*da6c28aaSamw 		if (key_len > handle->ht_key_size)
245*da6c28aaSamw 			return (NULL);
246*da6c28aaSamw 
247*da6c28aaSamw 		/* Include the null terminator */
248*da6c28aaSamw 		++key_len;
249*da6c28aaSamw 	}
250*da6c28aaSamw 
251*da6c28aaSamw 	msize = key_len + sizeof (HT_ITEM);
252*da6c28aaSamw 
253*da6c28aaSamw 	if ((item = malloc(msize)) == 0)
254*da6c28aaSamw 		return (NULL);
255*da6c28aaSamw 
256*da6c28aaSamw 	item->hi_key = (char *)item + sizeof (HT_ITEM);
257*da6c28aaSamw 	(void) memcpy(item->hi_key, key, key_len);
258*da6c28aaSamw 	item->hi_data = (void *)data;
259*da6c28aaSamw 	item->hi_flags = 0;
260*da6c28aaSamw 
261*da6c28aaSamw 	h_index = handle->ht_hash(handle, key);
262*da6c28aaSamw 
263*da6c28aaSamw 	/*
264*da6c28aaSamw 	 * Add to the front of the list.
265*da6c28aaSamw 	 */
266*da6c28aaSamw 	item->hi_next = handle->ht_table[h_index].he_head;
267*da6c28aaSamw 	handle->ht_table[h_index].he_head = item;
268*da6c28aaSamw 
269*da6c28aaSamw 	handle->ht_table[h_index].he_count++;
270*da6c28aaSamw 	handle->ht_total_items++;
271*da6c28aaSamw 	handle->ht_sequence++;
272*da6c28aaSamw 
273*da6c28aaSamw 	return (item);
274*da6c28aaSamw }
275*da6c28aaSamw 
276*da6c28aaSamw 
277*da6c28aaSamw /*
278*da6c28aaSamw  * ht_replace_item
279*da6c28aaSamw  *
280*da6c28aaSamw  * Replace an item in a hash table. The item associated with key is removed
281*da6c28aaSamw  * using ht_remove_item and a new item is added using ht_add_item. We rely
282*da6c28aaSamw  * on parameter validation in ht_remove_item and ht_add_item.
283*da6c28aaSamw  *
284*da6c28aaSamw  * The table sequence number may be modified here.
285*da6c28aaSamw  */
286*da6c28aaSamw HT_ITEM *
ht_replace_item(HT_HANDLE * handle,const char * key,const void * data)287*da6c28aaSamw ht_replace_item(HT_HANDLE *handle, const char *key, const void *data)
288*da6c28aaSamw {
289*da6c28aaSamw 	(void) ht_remove_item(handle, key);
290*da6c28aaSamw 
291*da6c28aaSamw 	return (ht_add_item(handle, key, data));
292*da6c28aaSamw }
293*da6c28aaSamw 
294*da6c28aaSamw 
295*da6c28aaSamw /*
296*da6c28aaSamw  * ht_remove_item
297*da6c28aaSamw  *
298*da6c28aaSamw  * Remove an item from a hash table. If there are duplicate keys, then the
299*da6c28aaSamw  * first key found will be deleted. Note that the data pointer is never
300*da6c28aaSamw  * dereferenced.  If a callback is installed, it will be invoked and the
301*da6c28aaSamw  * return value will be null. Otherwise, the data pointer supplied by the
302*da6c28aaSamw  * application will be returned. If there is an error, a null pointer will
303*da6c28aaSamw  * be returned.
304*da6c28aaSamw  *
305*da6c28aaSamw  * The table sequence number may be modified here.
306*da6c28aaSamw  */
307*da6c28aaSamw void *
ht_remove_item(HT_HANDLE * handle,const char * key)308*da6c28aaSamw ht_remove_item(HT_HANDLE *handle, const char *key)
309*da6c28aaSamw {
310*da6c28aaSamw 	size_t h_index;
311*da6c28aaSamw 	HT_ITEM *cur, *prev;
312*da6c28aaSamw 	int key_len;
313*da6c28aaSamw 	void *data = 0;
314*da6c28aaSamw 
315*da6c28aaSamw 	if (handle == 0 || key == 0)
316*da6c28aaSamw 		return (NULL);
317*da6c28aaSamw 
318*da6c28aaSamw 	if ((handle->ht_flags & HTHF_FIXED_KEY) == 0)
319*da6c28aaSamw 		key_len = strlen(key) + 1;
320*da6c28aaSamw 	else
321*da6c28aaSamw 		key_len = handle->ht_key_size;
322*da6c28aaSamw 
323*da6c28aaSamw 	h_index = handle->ht_hash(handle, key);
324*da6c28aaSamw 
325*da6c28aaSamw 	cur = handle->ht_table[h_index].he_head;
326*da6c28aaSamw 	prev = 0;
327*da6c28aaSamw 
328*da6c28aaSamw 	while (cur) {
329*da6c28aaSamw 		if (!(cur->hi_flags & HTIF_MARKED_DELETED) &&
330*da6c28aaSamw 		    (handle->ht_cmp(cur->hi_key, key, key_len) == 0)) {
331*da6c28aaSamw 			/* found key */
332*da6c28aaSamw 			if (prev == 0)
333*da6c28aaSamw 				handle->ht_table[h_index].he_head =
334*da6c28aaSamw 				    cur->hi_next;
335*da6c28aaSamw 			else
336*da6c28aaSamw 				prev->hi_next = cur->hi_next;
337*da6c28aaSamw 
338*da6c28aaSamw 			if (handle->ht_callback)
339*da6c28aaSamw 				handle->ht_callback(cur);
340*da6c28aaSamw 			else
341*da6c28aaSamw 				data = cur->hi_data;
342*da6c28aaSamw 
343*da6c28aaSamw 			/*
344*da6c28aaSamw 			 * Since the key and the item were allocated as
345*da6c28aaSamw 			 * a single chunk, we only need one free here.
346*da6c28aaSamw 			 */
347*da6c28aaSamw 			free(cur);
348*da6c28aaSamw 
349*da6c28aaSamw 			handle->ht_table[h_index].he_count--;
350*da6c28aaSamw 			handle->ht_total_items--;
351*da6c28aaSamw 			handle->ht_sequence++;
352*da6c28aaSamw 			break;
353*da6c28aaSamw 		}
354*da6c28aaSamw 
355*da6c28aaSamw 		prev = cur;
356*da6c28aaSamw 		cur = cur->hi_next;
357*da6c28aaSamw 	}
358*da6c28aaSamw 
359*da6c28aaSamw 	return (data);
360*da6c28aaSamw }
361*da6c28aaSamw 
362*da6c28aaSamw /*
363*da6c28aaSamw  * ht_find_item
364*da6c28aaSamw  *
365*da6c28aaSamw  * Find an item in a hash table. If there are duplicate keys then the
366*da6c28aaSamw  * first item found (which will be the last one added) will be returned.
367*da6c28aaSamw  *
368*da6c28aaSamw  * Returns a pointer to an item. Otherwise returns a null pointer to
369*da6c28aaSamw  * indicate an error or that the key didn't match anything in the table.
370*da6c28aaSamw  */
371*da6c28aaSamw HT_ITEM *
ht_find_item(HT_HANDLE * handle,const char * key)372*da6c28aaSamw ht_find_item(HT_HANDLE *handle, const char *key)
373*da6c28aaSamw {
374*da6c28aaSamw 	size_t h_index;
375*da6c28aaSamw 	HT_ITEM *cur;
376*da6c28aaSamw 	int key_len;
377*da6c28aaSamw 
378*da6c28aaSamw 	if (handle == 0 || key == 0)
379*da6c28aaSamw 		return (NULL);
380*da6c28aaSamw 
381*da6c28aaSamw 	if ((handle->ht_flags & HTHF_FIXED_KEY) == 0)
382*da6c28aaSamw 		key_len = strlen(key) + 1;
383*da6c28aaSamw 	else
384*da6c28aaSamw 		key_len = handle->ht_key_size;
385*da6c28aaSamw 
386*da6c28aaSamw 	h_index = handle->ht_hash(handle, key);
387*da6c28aaSamw 	cur = handle->ht_table[h_index].he_head;
388*da6c28aaSamw 
389*da6c28aaSamw 	while (cur) {
390*da6c28aaSamw 		if (!(cur->hi_flags & HTIF_MARKED_DELETED) &&
391*da6c28aaSamw 		    (handle->ht_cmp(cur->hi_key, key, key_len) == 0))
392*da6c28aaSamw 			return (cur);
393*da6c28aaSamw 
394*da6c28aaSamw 		cur = cur->hi_next;
395*da6c28aaSamw 	}
396*da6c28aaSamw 
397*da6c28aaSamw 	return (NULL);
398*da6c28aaSamw }
399*da6c28aaSamw 
400*da6c28aaSamw 
401*da6c28aaSamw /*
402*da6c28aaSamw  * ht_register_callback
403*da6c28aaSamw  *
404*da6c28aaSamw  * Register an application callback function that can be used to process
405*da6c28aaSamw  * an item when it is removed from the table, i.e. free any memory
406*da6c28aaSamw  * allocated for that data item.
407*da6c28aaSamw  *
408*da6c28aaSamw  * The previous callback function pointer, which may be null, before
409*da6c28aaSamw  * registering the new one. This provides the caller with the option to
410*da6c28aaSamw  * restore a previous callback as required.
411*da6c28aaSamw  */
412*da6c28aaSamw HT_CALLBACK
ht_register_callback(HT_HANDLE * handle,HT_CALLBACK callback)413*da6c28aaSamw ht_register_callback(HT_HANDLE *handle, HT_CALLBACK callback)
414*da6c28aaSamw {
415*da6c28aaSamw 	HT_CALLBACK old_callback;
416*da6c28aaSamw 
417*da6c28aaSamw 	if (handle == 0)
418*da6c28aaSamw 		return (NULL);
419*da6c28aaSamw 
420*da6c28aaSamw 	old_callback = handle->ht_callback;
421*da6c28aaSamw 	handle->ht_callback = callback;
422*da6c28aaSamw 
423*da6c28aaSamw 	return (old_callback);
424*da6c28aaSamw }
425*da6c28aaSamw 
426*da6c28aaSamw 
427*da6c28aaSamw /*
428*da6c28aaSamw  * ht_clean_table
429*da6c28aaSamw  *
430*da6c28aaSamw  * This function removes all the items that are marked for deletion. Note
431*da6c28aaSamw  * that this will invoke the callback, if one has been installed. If this
432*da6c28aaSamw  * call is used, the callback mechanism is the only way for an application
433*da6c28aaSamw  * to free the item data if it was dynamically allocated.
434*da6c28aaSamw  *
435*da6c28aaSamw  * The table sequence number may be modified here.
436*da6c28aaSamw  *
437*da6c28aaSamw  * Returns 0 if the handle is valid; otherwise returns -1.
438*da6c28aaSamw  */
439*da6c28aaSamw size_t
ht_clean_table(HT_HANDLE * handle)440*da6c28aaSamw ht_clean_table(HT_HANDLE *handle)
441*da6c28aaSamw {
442*da6c28aaSamw 	size_t i;
443*da6c28aaSamw 	HT_ITEM *cur, *prev;
444*da6c28aaSamw 
445*da6c28aaSamw 	if (handle == 0)
446*da6c28aaSamw 		return ((size_t)-1);
447*da6c28aaSamw 
448*da6c28aaSamw 	for (i = 0; i < handle->ht_table_size; i++) {
449*da6c28aaSamw 		cur = handle->ht_table[i].he_head;
450*da6c28aaSamw 		prev = 0;
451*da6c28aaSamw 
452*da6c28aaSamw 		while (cur) {
453*da6c28aaSamw 			if (cur->hi_flags & HTIF_MARKED_DELETED) {
454*da6c28aaSamw 				/*
455*da6c28aaSamw 				 * We have a marked item: remove it.
456*da6c28aaSamw 				 */
457*da6c28aaSamw 				if (prev == 0)
458*da6c28aaSamw 					handle->ht_table[i].he_head =
459*da6c28aaSamw 					    cur->hi_next;
460*da6c28aaSamw 				else
461*da6c28aaSamw 					prev->hi_next = cur->hi_next;
462*da6c28aaSamw 
463*da6c28aaSamw 				if (handle->ht_callback)
464*da6c28aaSamw 					handle->ht_callback(cur);
465*da6c28aaSamw 
466*da6c28aaSamw 				/*
467*da6c28aaSamw 				 * Since the key and the item were allocated as
468*da6c28aaSamw 				 * a single chunk, we only need one free here.
469*da6c28aaSamw 				 */
470*da6c28aaSamw 				free(cur);
471*da6c28aaSamw 
472*da6c28aaSamw 				handle->ht_table[i].he_count--;
473*da6c28aaSamw 				handle->ht_sequence++;
474*da6c28aaSamw 
475*da6c28aaSamw 				if (prev == 0)
476*da6c28aaSamw 					cur = handle->ht_table[i].he_head;
477*da6c28aaSamw 				else
478*da6c28aaSamw 					cur = prev->hi_next;
479*da6c28aaSamw 				continue;
480*da6c28aaSamw 			}
481*da6c28aaSamw 
482*da6c28aaSamw 			prev = cur;
483*da6c28aaSamw 			cur = cur->hi_next;
484*da6c28aaSamw 		}
485*da6c28aaSamw 	}
486*da6c28aaSamw 
487*da6c28aaSamw 	return (0);
488*da6c28aaSamw }
489*da6c28aaSamw 
490*da6c28aaSamw 
491*da6c28aaSamw /*
492*da6c28aaSamw  * ht_mark_delete
493*da6c28aaSamw  *
494*da6c28aaSamw  * This function marks an item for deletion, which may be useful when
495*da6c28aaSamw  * using findfirst/findnext to avoid modifying the table during the
496*da6c28aaSamw  * table scan. Marked items can be removed later using ht_clean_table.
497*da6c28aaSamw  */
498*da6c28aaSamw void
ht_mark_delete(HT_HANDLE * handle,HT_ITEM * item)499*da6c28aaSamw ht_mark_delete(HT_HANDLE *handle, HT_ITEM *item)
500*da6c28aaSamw {
501*da6c28aaSamw 	if (handle && item) {
502*da6c28aaSamw 		item->hi_flags |= HTIF_MARKED_DELETED;
503*da6c28aaSamw 		handle->ht_total_items--;
504*da6c28aaSamw 	}
505*da6c28aaSamw }
506*da6c28aaSamw 
507*da6c28aaSamw /*
508*da6c28aaSamw  * ht_clear_delete
509*da6c28aaSamw  *
510*da6c28aaSamw  * This function clear an item from marked for deletion list.
511*da6c28aaSamw  */
512*da6c28aaSamw void
ht_clear_delete(HT_HANDLE * handle,HT_ITEM * item)513*da6c28aaSamw ht_clear_delete(HT_HANDLE *handle, HT_ITEM *item)
514*da6c28aaSamw {
515*da6c28aaSamw 	if (handle && item) {
516*da6c28aaSamw 		item->hi_flags &= ~HTIF_MARKED_DELETED;
517*da6c28aaSamw 		handle->ht_total_items++;
518*da6c28aaSamw 	}
519*da6c28aaSamw }
520*da6c28aaSamw 
521*da6c28aaSamw /*
522*da6c28aaSamw  * ht_bucket_search
523*da6c28aaSamw  *
524*da6c28aaSamw  * Returns first item which is not marked as deleted
525*da6c28aaSamw  * in the specified bucket by 'head'
526*da6c28aaSamw  */
527*da6c28aaSamw static HT_ITEM *
ht_bucket_search(HT_ITEM * head)528*da6c28aaSamw ht_bucket_search(HT_ITEM *head)
529*da6c28aaSamw {
530*da6c28aaSamw 	HT_ITEM *item = head;
531*da6c28aaSamw 	while ((item != 0) && (item->hi_flags & HTIF_MARKED_DELETED))
532*da6c28aaSamw 		item = item->hi_next;
533*da6c28aaSamw 
534*da6c28aaSamw 	return (item);
535*da6c28aaSamw }
536*da6c28aaSamw 
537*da6c28aaSamw /*
538*da6c28aaSamw  * ht_findfirst
539*da6c28aaSamw  *
540*da6c28aaSamw  * This function is used to begin an iteration through the hash table.
541*da6c28aaSamw  * The iterator is initialized and the first item in the table (as
542*da6c28aaSamw  * determined by the hash algorithm) is returned. The current sequence
543*da6c28aaSamw  * number is stored in the iterator to determine whether or not the
544*da6c28aaSamw  * the table has changed between calls. If the table is empty, a null
545*da6c28aaSamw  * pointer is returned.
546*da6c28aaSamw  */
547*da6c28aaSamw HT_ITEM *
ht_findfirst(HT_HANDLE * handle,HT_ITERATOR * iterator)548*da6c28aaSamw ht_findfirst(HT_HANDLE *handle, HT_ITERATOR *iterator)
549*da6c28aaSamw {
550*da6c28aaSamw 	HT_ITEM *item;
551*da6c28aaSamw 	size_t h_index;
552*da6c28aaSamw 
553*da6c28aaSamw 	if (handle == 0 || iterator == 0 || handle->ht_total_items == 0)
554*da6c28aaSamw 		return (NULL);
555*da6c28aaSamw 
556*da6c28aaSamw 	(void) memset(iterator, 0, sizeof (HT_ITERATOR));
557*da6c28aaSamw 	iterator->hti_handle = handle;
558*da6c28aaSamw 	iterator->hti_sequence = handle->ht_sequence;
559*da6c28aaSamw 
560*da6c28aaSamw 	for (h_index = 0; h_index < handle->ht_table_size; ++h_index) {
561*da6c28aaSamw 		item = ht_bucket_search(handle->ht_table[h_index].he_head);
562*da6c28aaSamw 		if (item != 0) {
563*da6c28aaSamw 			iterator->hti_index = h_index;
564*da6c28aaSamw 			iterator->hti_item = item;
565*da6c28aaSamw 			return (item);
566*da6c28aaSamw 		}
567*da6c28aaSamw 	}
568*da6c28aaSamw 
569*da6c28aaSamw 	return (NULL);
570*da6c28aaSamw }
571*da6c28aaSamw 
572*da6c28aaSamw /*
573*da6c28aaSamw  * ht_findnext
574*da6c28aaSamw  *
575*da6c28aaSamw  * Find the next item in the table for the given iterator. Iterators must
576*da6c28aaSamw  * be initialized by ht_findfirst, which will also return the first item
577*da6c28aaSamw  * in the table. If an item is available, a pointer to it is returned.
578*da6c28aaSamw  * Otherwise a null pointer is returned. A null pointer may indicate:
579*da6c28aaSamw  *
580*da6c28aaSamw  *	- an invalid iterator (i.e. ht_findfirst has not been called)
581*da6c28aaSamw  *	- the table has changed since the previous findfirst/findnext
582*da6c28aaSamw  *	- the entire table has been traversed
583*da6c28aaSamw  *
584*da6c28aaSamw  * The caller can use ht_get_total_items to determine whether or not all
585*da6c28aaSamw  * of the items in the table have been visited.
586*da6c28aaSamw  */
587*da6c28aaSamw HT_ITEM *
ht_findnext(HT_ITERATOR * iterator)588*da6c28aaSamw ht_findnext(HT_ITERATOR *iterator)
589*da6c28aaSamw {
590*da6c28aaSamw 	HT_HANDLE *handle;
591*da6c28aaSamw 	HT_ITEM *item;
592*da6c28aaSamw 	size_t total;
593*da6c28aaSamw 	size_t index;
594*da6c28aaSamw 
595*da6c28aaSamw 	if (iterator == 0 || iterator->hti_handle == 0 ||
596*da6c28aaSamw 	    iterator->hti_sequence == 0) {
597*da6c28aaSamw 		/* Invalid iterator */
598*da6c28aaSamw 		return (NULL);
599*da6c28aaSamw 	}
600*da6c28aaSamw 
601*da6c28aaSamw 	handle = iterator->hti_handle;
602*da6c28aaSamw 
603*da6c28aaSamw 	if (iterator->hti_item == 0 ||
604*da6c28aaSamw 	    iterator->hti_sequence != handle->ht_sequence) {
605*da6c28aaSamw 		/*
606*da6c28aaSamw 		 * No more items or the table has changed
607*da6c28aaSamw 		 * since the last call.
608*da6c28aaSamw 		 */
609*da6c28aaSamw 		return (NULL);
610*da6c28aaSamw 	}
611*da6c28aaSamw 
612*da6c28aaSamw 	/*
613*da6c28aaSamw 	 * Check for another item in the current bucket.
614*da6c28aaSamw 	 */
615*da6c28aaSamw 	item = ht_bucket_search(iterator->hti_item->hi_next);
616*da6c28aaSamw 	if (item != 0) {
617*da6c28aaSamw 		iterator->hti_item = item;
618*da6c28aaSamw 		return (item);
619*da6c28aaSamw 	}
620*da6c28aaSamw 
621*da6c28aaSamw 	/*
622*da6c28aaSamw 	 * Nothing else in the current bucket. Look for another
623*da6c28aaSamw 	 * bucket with something in it and return the head item.
624*da6c28aaSamw 	 */
625*da6c28aaSamw 	total = handle->ht_table_size;
626*da6c28aaSamw 	for (index = iterator->hti_index + 1; index < total; ++index) {
627*da6c28aaSamw 		item = ht_bucket_search(handle->ht_table[index].he_head);
628*da6c28aaSamw 		if (item != 0) {
629*da6c28aaSamw 			iterator->hti_index = index;
630*da6c28aaSamw 			iterator->hti_item = item;
631*da6c28aaSamw 			return (item);
632*da6c28aaSamw 		}
633*da6c28aaSamw 	}
634*da6c28aaSamw 
635*da6c28aaSamw 	iterator->hti_index = 0;
636*da6c28aaSamw 	iterator->hti_item = 0;
637*da6c28aaSamw 	iterator->hti_sequence = 0;
638*da6c28aaSamw 	return (NULL);
639*da6c28aaSamw }
640