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 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 * 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 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 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 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 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 * 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 * 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 * 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 * 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 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 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 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 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 * 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 * 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 * 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