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