00001
00015
00016
00017 #include <stdlib.h>
00018 #include <stdio.h>
00019 #include <string.h>
00020 #include <errno.h>
00021 #include "hashtable.h"
00022 #include "linked_list.h"
00023 #include "log.h"
00024 #include "common.h"
00025 #include "collection.h"
00026 #include "thread.h"
00027 #include "util.h"
00028
00029 #include "config.h"
00030 #ifdef CP_HAS_STRINGS_H
00031 #include <strings.h>
00032 #endif
00033
00034 #ifdef __OpenBSD__
00035 #include <sys/types.h>
00036 #include <sys/time.h>
00037 #include <unistd.h>
00038 #endif
00039
00040 #ifndef CP_HASHTABLE_MULTIPLE_VALUES
00041 #define CP_HASHTABLE_MULTIPLE_VALUES 1
00042 #endif
00043
00044 static int table_sizes[] =
00045 {
00046 5, 7, 11, 23, 47,
00047 79, 97, 157, 197, 299,
00048 397, 599, 797, 1297, 1597,
00049 2499, 3199, 4799, 6397, 9599,
00050 12799, 19199, 25597, 38399, 51199,
00051 76799, 102397, 153599, 204797, 306797,
00052 409597, 614399, 819199, 1288799, 1638397,
00053 2457599, 3276799, 4915217, 6553577, 9830393
00054 };
00055
00056 static int table_sizes_len = 40;
00057
00058
00059 unsigned long cp_hashtable_choose_size(unsigned long size_request)
00060 {
00061 unsigned long new_size;
00062
00063 if (table_sizes[table_sizes_len - 1] < size_request)
00064 {
00065 for (new_size = table_sizes[table_sizes_len - 1];
00066 new_size < size_request;
00067 new_size = new_size * 2 + 1);
00068 }
00069 else
00070 {
00071 int min = -1;
00072 int max = table_sizes_len - 1;
00073 int pos;
00074
00075 while (max > min + 1)
00076 {
00077 pos = (max + min + 1) / 2;
00078 if (table_sizes[pos] < size_request)
00079 min = pos;
00080 else
00081 max = pos;
00082 }
00083
00084 new_size = table_sizes[max];
00085 }
00086
00087 return new_size;
00088 }
00089
00090 cp_hashtable *cp_hashtable_create(unsigned long size_hint,
00091 cp_hashfunction hash_fn,
00092 cp_compare_fn compare_fn)
00093 {
00094 return cp_hashtable_create_by_option(0, size_hint, hash_fn,
00095 compare_fn, NULL, NULL, NULL, NULL);
00096 }
00097
00098
00099 cp_hashtable *
00100 cp_hashtable_create_copy_mode(unsigned long size_hint,
00101 cp_hashfunction hash_fn,
00102 cp_compare_fn compare_fn,
00103 cp_copy_fn copy_key,
00104 cp_destructor_fn free_key,
00105 cp_copy_fn copy_value,
00106 cp_destructor_fn free_value)
00107 {
00108 return cp_hashtable_create_by_option(COLLECTION_MODE_DEEP |
00109 COLLECTION_MODE_COPY,
00110 size_hint,
00111 hash_fn,
00112 compare_fn,
00113 copy_key,
00114 free_key,
00115 copy_value,
00116 free_value);
00117 }
00118
00119 cp_hashtable *
00120 cp_hashtable_create_by_option(int mode, unsigned long size_hint,
00121 cp_hashfunction hash_fn,
00122 cp_compare_fn compare_fn,
00123 cp_copy_fn copy_key,
00124 cp_destructor_fn free_key,
00125 cp_copy_fn copy_value,
00126 cp_destructor_fn free_value)
00127 {
00128 cp_hashtable *table;
00129
00130 table = (cp_hashtable *) calloc(1, sizeof(cp_hashtable));
00131 if (table == NULL)
00132 {
00133 errno = ENOMEM;
00134 return NULL;
00135 }
00136
00137 table->table_size = cp_hashtable_choose_size(size_hint);
00138 table->items = 0;
00139 table->table = (cp_hashtable_entry **)
00140 calloc(table->table_size, sizeof(cp_hashtable_entry *));
00141 if (table->table == NULL)
00142 {
00143 errno = ENOMEM;
00144 return NULL;
00145 }
00146
00147 table->hash_fn = hash_fn;
00148 table->compare_fn = compare_fn;
00149 table->copy_key = copy_key;
00150 table->copy_value = copy_value;
00151 table->free_key = free_key;
00152 table->free_value = free_value;
00153
00154 table->mode = mode;
00155
00156 table->min_size = size_hint;
00157 table->fill_factor_min = CP_HASHTABLE_DEFAULT_MIN_FILL_FACTOR;
00158 table->fill_factor_max = CP_HASHTABLE_DEFAULT_MAX_FILL_FACTOR;
00159
00160 table->resizing = 0;
00161
00162 table->lock = malloc(sizeof(cp_lock));
00163 if (table->lock == NULL)
00164 {
00165 free(table->table);
00166 free(table);
00167 errno = ENOMEM;
00168 return NULL;
00169 }
00170
00171 if (cp_lock_init(table->lock, NULL))
00172 {
00173 free(table->lock);
00174 free(table->table);
00175 free(table);
00176 return NULL;
00177 }
00178
00179 return table;
00180 }
00181
00182 cp_hashtable_entry *
00183 cp_hashtable_create_entry(cp_hashtable *table,
00184 int mode,
00185 void *key,
00186 void *value,
00187 long hashcode)
00188 {
00189 cp_hashtable_entry *entry;
00190
00191 entry = (cp_hashtable_entry *) malloc(sizeof(cp_hashtable_entry));
00192 if (entry == NULL)
00193 {
00194 errno = ENOMEM;
00195 return NULL;
00196 }
00197
00198 if (mode & COLLECTION_MODE_COPY)
00199 {
00200 entry->key = table->copy_key ? (*table->copy_key)(key) : key;
00201 entry->value = table->copy_value ? (*table->copy_value)(value) :value;
00202 }
00203 else
00204 {
00205 entry->key = key;
00206 entry->value = value;
00207 }
00208
00209 entry->hashcode = hashcode;
00210 entry->next = NULL;
00211
00212 return entry;
00213 }
00214
00215
00216 void cp_hashtable_destroy(cp_hashtable *table)
00217 {
00218 long i;
00219 cp_hashtable_entry *entry, *next;
00220
00221 table->mode |= COLLECTION_MODE_NORESIZE;
00222
00223 if (table->resizing)
00224 {
00225 struct timeval timeout;
00226 timeout.tv_sec = 0;
00227 timeout.tv_usec = 10;
00228 while (table->resizing)
00229 select(0, NULL, NULL, NULL, &timeout);
00230 }
00231
00232 for (i = 0; i < table->table_size; i++)
00233 {
00234 entry = table->table[i];
00235 while (entry != NULL)
00236 {
00237 next = entry->next;
00238 if (table->mode & COLLECTION_MODE_DEEP)
00239 {
00240 if (table->free_key)
00241 (*table->free_key)(entry->key);
00242 if (table->free_value)
00243 (*table->free_value)(entry->value);
00244 }
00245 free(entry);
00246 entry = next;
00247 }
00248 }
00249
00250 free(table->table);
00251 cp_lock_destroy(table->lock);
00252 free(table->lock);
00253 free(table);
00254 }
00255
00256 void cp_hashtable_destroy_deep(cp_hashtable *table)
00257 {
00258 cp_hashtable_set_mode(table, COLLECTION_MODE_DEEP);
00259 cp_hashtable_destroy_custom(table, table->free_key, table->free_value);
00260 }
00261
00262 void cp_hashtable_destroy_custom(cp_hashtable *table, cp_destructor_fn dk, cp_destructor_fn dv)
00263 {
00264 long i;
00265 cp_hashtable_entry *entry, *next;
00266
00267 if (table->resizing)
00268 {
00269 struct timeval timeout;
00270 timeout.tv_sec = 0;
00271 timeout.tv_usec = 10;
00272 while (table->resizing)
00273 select(0, NULL, NULL, NULL, &timeout);
00274 }
00275
00276 for (i = 0; i < table->table_size; i++)
00277 {
00278 entry = table->table[i];
00279 while (entry != NULL)
00280 {
00281 next = entry->next;
00282 if (dk) (*dk)(entry->key);
00283 if (dv) (*dv)(entry->value);
00284 free(entry);
00285 entry = next;
00286 }
00287 }
00288
00289 free(table->table);
00290 cp_lock_destroy(table->lock);
00291 free(table->lock);
00292 free(table);
00293 }
00294
00295
00296 void cp_hashtable_destroy_shallow(cp_hashtable *table)
00297 {
00298 cp_hashtable_unset_mode(table, COLLECTION_MODE_DEEP);
00299 cp_hashtable_destroy(table);
00300 }
00301
00302 int cp_hashtable_lock_internal(cp_hashtable *table, int type)
00303 {
00304 int rc;
00305
00306 switch (type)
00307 {
00308 case COLLECTION_LOCK_READ:
00309 rc = cp_lock_rdlock(table->lock);
00310 break;
00311
00312 case COLLECTION_LOCK_WRITE:
00313 rc = cp_lock_wrlock(table->lock);
00314 break;
00315
00316 case COLLECTION_LOCK_NONE:
00317 rc = 0;
00318 break;
00319
00320 default:
00321 rc = EINVAL;
00322 break;
00323 }
00324
00325 return rc;
00326 }
00327
00328 int cp_hashtable_unlock_internal(cp_hashtable *table)
00329 {
00330 return cp_lock_unlock(table->lock);
00331 }
00332
00333 int cp_hashtable_txlock(cp_hashtable *table, int type)
00334 {
00335 if (table->mode & COLLECTION_MODE_NOSYNC) return 0;
00336 if (table->mode & COLLECTION_MODE_IN_TRANSACTION &&
00337 table->txtype == COLLECTION_LOCK_WRITE)
00338 {
00339 cp_thread self = cp_thread_self();
00340 if (cp_thread_equal(self, table->txowner)) return 0;
00341 }
00342 return cp_hashtable_lock_internal(table, type);
00343 }
00344
00345 int cp_hashtable_txunlock(cp_hashtable *table)
00346 {
00347 if (table->mode & COLLECTION_MODE_NOSYNC) return 0;
00348 if (table->mode & COLLECTION_MODE_IN_TRANSACTION &&
00349 table->txtype == COLLECTION_LOCK_WRITE)
00350 {
00351 cp_thread self = cp_thread_self();
00352 if (cp_thread_equal(self, table->txowner)) return 0;
00353 }
00354 return cp_hashtable_unlock_internal(table);
00355 }
00356
00357
00358 int cp_hashtable_lock(cp_hashtable *table, int type)
00359 {
00360 int rc;
00361 if ((table->mode & COLLECTION_MODE_NOSYNC)) return EINVAL;
00362 if ((rc = cp_hashtable_lock_internal(table, type))) return rc;
00363 table->txtype = type;
00364 table->txowner = cp_thread_self();
00365 table->mode |= COLLECTION_MODE_IN_TRANSACTION;
00366 return 0;
00367 }
00368
00369
00370 int cp_hashtable_unlock(cp_hashtable *table)
00371 {
00372 cp_thread self = cp_thread_self();
00373 if (table->txowner == self)
00374 {
00375 table->txtype = 0;
00376 table->txowner = 0;
00377 table->mode ^= COLLECTION_MODE_IN_TRANSACTION;
00378 }
00379 else if (table->txtype == COLLECTION_LOCK_WRITE)
00380 return -1;
00381 return cp_hashtable_unlock_internal(table);
00382 }
00383
00384
00385
00386 int cp_hashtable_set_mode(cp_hashtable *table, int mode)
00387 {
00388 int syncbit;
00389
00390 if ((table->mode & COLLECTION_MODE_IN_TRANSACTION) &&
00391 (mode & COLLECTION_MODE_NOSYNC)) return EINVAL;
00392
00393 syncbit = table->mode & COLLECTION_MODE_NOSYNC;
00394 if (!syncbit)
00395 cp_hashtable_txlock(table, COLLECTION_LOCK_WRITE);
00396 table->mode |= mode;
00397 if (!syncbit)
00398 cp_hashtable_txunlock(table);
00399
00400 return 0;
00401 }
00402
00403 int cp_hashtable_get_mode(cp_hashtable *table)
00404 {
00405 return table->mode;
00406 }
00407
00408 int cp_hashtable_unset_mode(cp_hashtable *table, int mode)
00409 {
00410 int syncbit = table->mode & COLLECTION_MODE_NOSYNC;
00411 if (!syncbit)
00412 cp_hashtable_txlock(table, COLLECTION_LOCK_WRITE);
00413 table->mode &= table->mode ^ mode;
00414 if (!syncbit)
00415 cp_hashtable_txunlock(table);
00416 return 0;
00417 }
00418
00430 void *lookup_internal(cp_hashtable *table, void *key, long code, int option, int resize)
00431 {
00432 cp_hashtable_entry *entry;
00433 void *ret = NULL;
00434
00435 entry = resize ? table->resize_table[code % table->resize_len]
00436 : table->table[code % table->table_size];
00437
00438 while (entry != NULL && (*table->compare_fn)(key, entry->key))
00439 entry = entry->next;
00440
00441 if (entry)
00442 {
00443 #if CP_HASHTABLE_MULTIPLE_VALUES
00444 if (option & COLLECTION_MODE_MULTIPLE_VALUES)
00445 {
00446 cp_list *l =
00447 cp_list_create_view(table->mode,
00448 NULL,
00449 table->copy_value,
00450 table->free_value,
00451 table->lock);
00452 cp_list_insert(l, entry->value);
00453 entry = entry->next;
00454 while (entry != NULL)
00455 {
00456 if ((*table->compare_fn)(key, entry->key) == 0)
00457 cp_list_append(l, entry->value);
00458
00459 entry = entry->next;
00460 }
00461
00462 ret = l;
00463 }
00464 else
00465 #endif
00466 ret = entry->value;
00467 }
00468
00469 return ret;
00470 }
00471
00481 void *cp_hashtable_get_by_option(cp_hashtable *table, void *key, int option)
00482 {
00483 long code;
00484 void *ret;
00485
00486 if (table == NULL || key == NULL)
00487 {
00488 errno = EINVAL;
00489 return NULL;
00490 }
00491
00492 if (cp_hashtable_txlock(table, COLLECTION_LOCK_READ)) return NULL;
00493
00494 code = abs((*table->hash_fn)(key));
00495 ret = lookup_internal(table, key, code, option, 0);
00496
00497 if (ret == NULL && table->resizing)
00498 ret = lookup_internal(table, key, code, option, 1);
00499
00500 cp_hashtable_txunlock(table);
00501
00502 return ret;
00503 }
00504
00516 void *cp_hashtable_resize_thread(void *tbl)
00517 {
00518 long i, old_size, index;
00519 long new_size;
00520 cp_hashtable_entry **new_table, **old_table;
00521 cp_hashtable_entry **insert, *entry;
00522 cp_hashtable *table = (cp_hashtable *) tbl;
00523
00524 new_size = table->table_size;
00525 new_table = table->table;
00526
00527 old_size = table->resize_len;
00528 old_table = table->resize_table;
00529
00530 #ifdef __TRACE__
00531 DEBUGMSG("resize %d to %d - resize thread starting\n", old_size, new_size);
00532 #endif
00533
00534
00535 for (i = 0; i < old_size; i++)
00536 {
00537
00538 cp_hashtable_txlock(table, COLLECTION_LOCK_WRITE);
00539 entry = old_table[i];
00540 while (entry != NULL)
00541 {
00542 index = entry->hashcode % new_size;
00543 insert = &new_table[index];
00544 while (*insert != NULL)
00545 insert = &(*insert)->next;
00546
00547 *insert = entry;
00548 entry = entry->next;
00549 (*insert)->next = NULL;
00550 }
00551 old_table[i] = NULL;
00552
00553
00554 cp_hashtable_txunlock(table);
00555 }
00556
00557
00558
00559 cp_hashtable_txlock(table, COLLECTION_LOCK_WRITE);
00560 free(table->resize_table); table->resize_table = NULL;
00561 table->resizing = 0;
00562
00563
00564 #ifdef __TRACE__
00565 DEBUGMSG("resize %d to %d - done\n", old_size, new_size);
00566 #endif
00567 cp_hashtable_txunlock(table);
00568
00569 return NULL;
00570 }
00571
00572 int cp_hashtable_set_min_size(cp_hashtable *table, int min_size)
00573 {
00574 if (cp_hashtable_txlock(table, COLLECTION_LOCK_WRITE)) return -1;
00575 table->min_size = min_size;
00576 cp_hashtable_txunlock(table);
00577 return 0;
00578 }
00579
00580
00581 int cp_hashtable_set_max_fill_factor(cp_hashtable *table, int fill_factor)
00582 {
00583 if (cp_hashtable_txlock(table, COLLECTION_LOCK_WRITE)) return -1;
00584 table->fill_factor_max = fill_factor;
00585 cp_hashtable_txunlock(table);
00586 return 0;
00587 }
00588
00589 int cp_hashtable_set_min_fill_factor(cp_hashtable *table, int fill_factor)
00590 {
00591 if (cp_hashtable_txlock(table, COLLECTION_LOCK_WRITE)) return -1;
00592 table->fill_factor_min = fill_factor;
00593 cp_hashtable_txunlock(table);
00594 return 0;
00595 }
00596
00597
00603 void *cp_hashtable_resize(cp_hashtable *table, long new_size)
00604 {
00605 cp_hashtable_entry **new_table;
00606
00607 if (table->resizing) return NULL;
00608
00609 new_table = (cp_hashtable_entry **) malloc(new_size * sizeof(cp_hashtable_entry *));
00610 if (new_table == NULL)
00611 {
00612 errno = ENOMEM;
00613 return NULL;
00614 }
00615 memset(new_table, 0, new_size * sizeof(cp_hashtable_entry *));
00616
00617 table->resize_table = table->table;
00618 table->resize_len = table->table_size;
00619 table->table = new_table;
00620 table->table_size = new_size;
00621 table->resizing = 1;
00622
00623 cp_thread_create(table->resize_thread, NULL, cp_hashtable_resize_thread, table);
00624 cp_thread_detach(table->resize_thread);
00625
00626 return NULL;
00627 }
00628
00638 void *cp_hashtable_resize_nosync(cp_hashtable *table, unsigned long new_size)
00639 {
00640 unsigned long old_size;
00641 cp_hashtable_entry **old_table;
00642 cp_hashtable_entry *entry, *next, **insert;
00643 unsigned long i, index;
00644
00645 old_size = table->table_size;
00646 old_table = table->table;
00647
00648 table->table_size = cp_hashtable_choose_size(new_size);
00649
00650 #ifdef __TRACE__
00651 DEBUGMSG("resizing table (nosync): %d to %d\n", old_size, table->table_size);
00652 #endif
00653
00654 table->table = (cp_hashtable_entry **) malloc(table->table_size * sizeof(cp_hashtable_entry *));
00655 memset(table->table, 0, table->table_size * sizeof(cp_hashtable_entry *));
00656
00657 if (table->table == NULL)
00658 {
00659 errno = ENOMEM;
00660 return NULL;
00661 }
00662
00663 for (i = 0; i < old_size; i++)
00664 {
00665 entry = old_table[i];
00666 while (entry != NULL)
00667 {
00668 index = entry->hashcode % table->table_size;
00669 next = entry->next;
00670 entry->next = NULL;
00671 insert = &table->table[index];
00672 while (*insert != NULL)
00673 insert = &(*insert)->next;
00674
00675 *insert = entry;
00676
00677 entry = next;
00678 }
00679 }
00680
00681 free(old_table);
00682
00683 return table;
00684 }
00685
00697 static cp_hashtable_entry **cp_hashtable_replace_internal(cp_hashtable *table,
00698 void *key,
00699 void *value,
00700 unsigned long code,
00701 int option,
00702 int resize)
00703 {
00704 cp_hashtable_entry **entry;
00705 unsigned long index;
00706
00707 index = resize ? code % table->resize_len : code % table->table_size;
00708
00709 entry = resize ? &table->resize_table[index] : &table->table[index];
00710 while (*entry != NULL)
00711 {
00712 #if CP_HASHTABLE_MULTIPLE_VALUES
00713 if (!(option & COLLECTION_MODE_MULTIPLE_VALUES) &&
00714 (*table->compare_fn)(key, (*entry)->key) == 0)
00715 #else
00716 if ((*table->compare_fn)(key, (*entry)->key) == 0)
00717 #endif
00718 {
00719 if (option & COLLECTION_MODE_DEEP)
00720 {
00721 if (table->free_key)
00722 (*table->free_key)((*entry)->key);
00723 if (table->free_value)
00724 (*table->free_value)((*entry)->value);
00725 (*entry)->key = key;
00726 }
00727
00728 if (option & COLLECTION_MODE_COPY)
00729 {
00730 (*entry)->key = table->copy_key ? (*table->copy_key)(key) : key;
00731 (*entry)->value = table->copy_value ? (*table->copy_value)(value) : value;
00732 }
00733 else
00734 (*entry)->value = value;
00735
00736 (*entry)->hashcode = code;
00737 break;
00738 }
00739
00740 entry = &(*entry)->next;
00741 }
00742
00743 return entry;
00744 }
00745
00746 void *cp_hashtable_put(cp_hashtable *table, void *key, void *value)
00747 {
00748 return cp_hashtable_put_by_option(table, key, value, table->mode);
00749 }
00750
00751 void *cp_hashtable_put_safe(cp_hashtable *table, void *key, void *value)
00752 {
00753 return cp_hashtable_put_by_option(table, key, value, table->mode | COLLECTION_MODE_DEEP);
00754 }
00755
00756 void *cp_hashtable_put_copy(cp_hashtable *table, void *key, void *value)
00757 {
00758 return cp_hashtable_put_by_option(table, key, value, table->mode | COLLECTION_MODE_COPY);
00759 }
00760
00761
00762
00763 void *cp_hashtable_put_by_option(cp_hashtable *table, void *key, void *value, int option)
00764 {
00765 unsigned long code;
00766 cp_hashtable_entry **entry;
00767 int syncbit;
00768 void *ret = NULL;
00769
00770 syncbit = table->mode & COLLECTION_MODE_NOSYNC;
00771 if (cp_hashtable_txlock(table, COLLECTION_LOCK_WRITE)) return NULL;
00772
00773 if ((option & COLLECTION_MODE_NORESIZE) == 0)
00774 {
00775 if ((table->items * 100) >
00776 (table->table_size * table->fill_factor_max))
00777 {
00778 int new_size = cp_hashtable_choose_size(table->table_size * 2);
00779 if (syncbit)
00780 cp_hashtable_resize_nosync(table, new_size);
00781 else
00782 cp_hashtable_resize(table, new_size);
00783 }
00784 }
00785
00786 code = abs((*table->hash_fn)(key));
00787
00788 entry = cp_hashtable_replace_internal(table, key, value, code, option, 0);
00789
00790 if (*entry == NULL && table->resizing)
00791 {
00792 cp_hashtable_entry **resize_entry;
00793
00794 resize_entry = cp_hashtable_replace_internal(table, key, value, code, option, 1);
00795 if (*resize_entry != NULL)
00796 entry = resize_entry;
00797
00798 }
00799
00800 if (*entry == NULL)
00801 {
00802
00803 *entry = cp_hashtable_create_entry(table, option, key, value, code);
00804 if (*entry)
00805 table->items++;
00806 }
00807
00808 if (*entry)
00809 ret = (*entry)->value;
00810
00811 if (!syncbit) cp_hashtable_txunlock(table);
00812
00813 return ret;
00814 }
00815
00829 void *cp_hashtable_remove_internal(cp_hashtable *table, void *key, long code, int mode, int resize)
00830 {
00831 cp_hashtable_entry **entry, *item;
00832 void *value = NULL;
00833
00834 entry = resize ? &table->resize_table[code % table->resize_len]
00835 : &table->table[code % table->table_size];
00836
00837 while (*entry != NULL)
00838 {
00839 if ((*table->compare_fn)(key, (*entry)->key) == 0)
00840 {
00841
00842
00843 table->items--;
00844 item = *entry;
00845 *entry = item->next;
00846
00847 value = item->value;
00848 if (mode & COLLECTION_MODE_DEEP)
00849 {
00850 if (table->free_key)
00851 (*table->free_key)(item->key);
00852 if (table->free_value)
00853 (*table->free_value)(item->value);
00854 }
00855
00856 free(item);
00857
00858 #if CP_HASHTABLE_MULTIPLE_VALUES
00859 if (!(mode & COLLECTION_MODE_MULTIPLE_VALUES))
00860 #endif
00861 break;
00862 }
00863
00864 entry = &(*entry)->next;
00865 }
00866
00867 return value;
00868 }
00869
00881 void *cp_hashtable_remove_by_mode(cp_hashtable *table, void *key, int mode)
00882 {
00883 long code;
00884 void *value = NULL;
00885 int syncbit = table->mode & COLLECTION_MODE_NOSYNC;
00886 if (cp_hashtable_txlock(table, COLLECTION_LOCK_WRITE)) return NULL;
00887
00888 code = abs((*table->hash_fn)(key));
00889
00890 value = cp_hashtable_remove_internal(table, key, code, mode, 0);
00891
00892 if (table->resizing)
00893 {
00894 void *resize_value;
00895 resize_value = cp_hashtable_remove_internal(table, key, code, mode, 1);
00896 if (value == NULL) value = resize_value;
00897 }
00898
00899 if ((table->mode & COLLECTION_MODE_NORESIZE) == 0)
00900 {
00901 if (table->table_size > table->min_size &&
00902 ((table->items * 100) <
00903 (table->table_size * table->fill_factor_min)))
00904 {
00905 int new_size = cp_hashtable_choose_size(table->table_size / 2);
00906 if (syncbit)
00907 cp_hashtable_resize_nosync(table, new_size);
00908 else
00909 cp_hashtable_resize(table, new_size);
00910 }
00911 }
00912
00913 if (!syncbit) cp_hashtable_txunlock(table);
00914
00915 return value;
00916 }
00917
00918 int cp_hashtable_remove_all(cp_hashtable *table)
00919 {
00920 long i;
00921 cp_hashtable_entry *entry, *next;
00922 int deepbit = table->mode & COLLECTION_MODE_DEEP;
00923 cp_destructor_fn dk = table->free_key;
00924 cp_destructor_fn dv = table->free_value;
00925
00926 if (cp_hashtable_txlock(table, COLLECTION_LOCK_WRITE)) return -1;
00927
00928 for (i = 0; i < table->table_size; i++)
00929 {
00930 if ((entry = table->table[i]) != NULL)
00931 {
00932 while (entry != NULL)
00933 {
00934 next = entry->next;
00935 if (deepbit)
00936 {
00937 if (dk) (*dk)(entry->key);
00938 if (dv) (*dv)(entry->value);
00939 }
00940 free(entry);
00941 entry = next;
00942 }
00943 }
00944 }
00945
00946 if (table->resizing)
00947 {
00948 for (i = 0; i < table->resize_len; i++)
00949 if ((entry = table->resize_table[i]) != NULL)
00950 while (entry != NULL)
00951 {
00952 next = entry->next;
00953 if (deepbit)
00954 {
00955 if (dk) (*dk)(entry->key);
00956 if (dv) (*dv)(entry->value);
00957 }
00958 free(entry);
00959 entry = next;
00960 }
00961 }
00962
00963 cp_hashtable_txunlock(table);
00964 return 0;
00965 }
00966
00967
00968 void *cp_hashtable_remove(cp_hashtable *table, void *key)
00969 {
00970 return cp_hashtable_remove_by_mode(table, key, table->mode);
00971 }
00972
00973
00980 int cp_hashtable_remove_deep(cp_hashtable *table, void *key)
00981 {
00982 return cp_hashtable_remove_by_mode(table, key
00983 , table->mode | COLLECTION_MODE_DEEP) != NULL;
00984 }
00985
00986 int cp_hashtable_contains(cp_hashtable *table, void *key)
00987 {
00988 int rc = 0;
00989
00990 if (table != NULL && key != NULL)
00991 {
00992 long code;
00993 void *val;
00994 int option;
00995 if (cp_hashtable_txlock(table, COLLECTION_LOCK_READ)) return -1;
00996
00997 option = table->mode & COLLECTION_MODE_MULTIPLE_VALUES;
00998 code = abs((*table->hash_fn)(key));
00999 val = lookup_internal(table, key, code, table->mode, 0);
01000
01001 if (val == NULL && table->resizing)
01002 val = lookup_internal(table, key, code, table->mode, 1);
01003
01004 if (val != NULL)
01005 {
01006 rc = 1;
01007 if (option) cp_list_destroy(val);
01008 }
01009
01010 cp_hashtable_txunlock(table);
01011 }
01012 else
01013 errno = EINVAL;
01014
01015 return rc;
01016 }
01017
01018
01019 void *cp_hashtable_get(cp_hashtable *table, void *key)
01020 {
01021 return cp_hashtable_get_by_option(table, key, table->mode);
01022 }
01023
01024 void **cp_hashtable_get_keys(cp_hashtable *table)
01025 {
01026 long i, j;
01027 void **keys;
01028 cp_hashtable_entry *entry = NULL;
01029 int rc = 0;
01030
01031 if (table == NULL)
01032 {
01033 errno = EINVAL;
01034 return NULL;
01035 }
01036
01037 if (cp_hashtable_txlock(table, COLLECTION_LOCK_READ)) return NULL;
01038
01039 keys = (void **) calloc(table->items, sizeof(void *));
01040 if (keys == NULL)
01041 {
01042 rc = ENOMEM;
01043 goto DONE;
01044 }
01045
01046 for (i = 0, j = 0; i < table->table_size; i++) \
01047 {
01048 entry = table->table[i];
01049 while (entry != NULL)
01050 {
01051 keys[j++] = entry->key;
01052 entry = entry->next;
01053 }
01054 }
01055
01056 if (table->resizing)
01057 for (i = 0; i < table->resize_len; i++)
01058 entry = table->resize_table[i];
01059 while (entry != NULL)
01060 {
01061 keys[j++] = entry->key;
01062 entry = entry->next;
01063 }
01064
01065 DONE:
01066 cp_hashtable_txunlock(table);
01067 if (rc) errno = rc;
01068
01069 return keys;
01070 }
01071
01072
01073 unsigned long cp_hashtable_count(cp_hashtable *table)
01074 {
01075 return (table) ? table->items : 0L;
01076 }
01077
01078
01079 void **cp_hashtable_get_values(cp_hashtable *table)
01080 {
01081 long i, j;
01082 void **values;
01083 cp_hashtable_entry *entry;
01084 int rc = 0;
01085
01086 if (cp_hashtable_txlock(table, COLLECTION_LOCK_READ)) return NULL;
01087
01088 values = (void **) malloc(table->items * sizeof(void *));
01089 if (values == NULL)
01090 {
01091 rc = ENOMEM;
01092 goto DONE;
01093 }
01094
01095 for (i = 0, j = 0; i < table->table_size; i++)
01096 {
01097 entry = table->table[i];
01098 while (entry != NULL)
01099 {
01100 values[j++] = entry->value;
01101 entry = entry->next;
01102 }
01103 }
01104
01105 if (table->resizing)
01106 {
01107 for (i = 0; i < table->resize_len; i++)
01108 {
01109 entry = table->resize_table[i];
01110 while (entry != NULL)
01111 {
01112 values[j++] = entry->value;
01113 entry = entry->next;
01114 }
01115 }
01116 }
01117
01118 DONE:
01119 cp_hashtable_txunlock(table);
01120 if (rc) errno = rc;
01121
01122 return values;
01123 }
01124
01125
01126
01127
01128
01129
01130 unsigned long cp_hash_int(void *i)
01131 {
01132 return (long) *((int *) i);
01133 }
01134
01135
01136 int cp_hash_compare_int(void *i, void *j)
01137 {
01138 return *((int *) i) - *((int *) j);
01139 }
01140
01141 unsigned long cp_hash_long(void *l)
01142 {
01143 return *((long *) l);
01144 }
01145
01146
01147 int cp_hash_compare_long(void *i, void *j)
01148 {
01149 long diff = *((long *) i) - *((long *) j);
01150 return diff > 0 ? 1 : diff < 0 ? -1 : 0;
01151 }
01152
01153 unsigned long cp_hash_addr(void *addr)
01154 {
01155 return (unsigned long) addr;
01156 }
01157
01158 int cp_hash_compare_addr(void *a1, void *a2)
01159 {
01160 return (long) a1 - (long) a2;
01161 }
01162
01163 unsigned long cp_hash_string(void *str)
01164 {
01165 int i;
01166 long res;
01167 char *_str;
01168
01169 if (str == NULL) return 0;
01170
01171 _str = (char *) str;
01172
01173 for (i = 0, res = 1; *_str != '\0'; _str++)
01174 res = res * HASH_SEED + *_str;
01175
01176 return res;
01177 }
01178
01179
01180 int cp_hash_compare_string(void *s1, void *s2)
01181 {
01182 if (s1 == NULL && s2 == NULL) return 0;
01183 if (s1 == NULL || s2 == NULL) return -1;
01184 return strcmp((char *) s1, (char *) s2);
01185 }
01186
01187 #define CP_CHAR_UC(x) ((x) >= 'a' && (x) <= 'z' ? ((x) - 'a' + 'A') : (x))
01188
01189 unsigned long cp_hash_istring(void *str)
01190 {
01191 int i;
01192 long res;
01193 char *_str;
01194
01195 if (str == NULL) return 0;
01196
01197 _str = (char *) str;
01198
01199 for (i = 0, res = 1; *_str != '\0'; _str++)
01200 res = res * HASH_SEED + CP_CHAR_UC(*_str);
01201
01202 return res;
01203 }
01204
01205 int cp_hash_compare_istring(void *s1, void *s2)
01206 {
01207 if (s1 == NULL && s2 == NULL) return 0;
01208 if (s1 == NULL || s2 == NULL) return -1;
01209 return strcasecmp((char *) s1, (char *) s2);
01210 }
01211
01212 void *cp_hash_copy_string(void *element)
01213 {
01214 char *src;
01215 char *dst;
01216 size_t len;
01217
01218 src = (char *) element;
01219 len = strlen(src) + 1;
01220 dst = (char *) malloc(len * sizeof(char));
01221
01222 if (dst == NULL) return NULL;
01223
01224 #ifdef CP_HAS_STRLCPY
01225 strlcpy(dst, src, len);
01226 #else
01227 strcpy(dst, src);
01228 #endif
01229 return dst;
01230 }
01231
01232 unsigned long cp_hash_float(void *addr)
01233 {
01234 unsigned long *p = (unsigned long *) addr;
01235 return *p;
01236 }
01237
01238 int cp_hash_compare_float(void *a1, void *a2)
01239 {
01240 float f1 = *(float *) a1;
01241 float f2 = *(float *) a2;
01242 if (f1 > f2) return 1;
01243 if (f1 < f2) return -1;
01244 return 0;
01245 }
01246
01247 unsigned long cp_hash_double(void *d)
01248 {
01249 unsigned long *p = (unsigned long *) d;
01250 if (sizeof(unsigned long) < sizeof(double))
01251 return p[0] ^ p[1];
01252 return *p;
01253 }
01254
01255 int cp_hash_compare_double(void *a1, void *a2)
01256 {
01257 double d1 = *(double *) a1;
01258 double d2 = *(double *) a2;
01259 if (d1 > d2) return 1;
01260 if (d1 < d2) return -1;
01261 return 0;
01262 }
01263