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