diff --git a/doc/userguide.txt b/doc/userguide.txt index 1123414..2e1b97c 100644 --- a/doc/userguide.txt +++ b/doc/userguide.txt @@ -290,7 +290,7 @@ right. You really need to pass 'a pointer' to the hash pointer: /* good */ - void add_user(struct my_struct **users, int user_id, char *name) { ... + void add_user(struct my_struct **users, int user_id, char *name) { ... HASH_ADD_INT(*users, id, s); } @@ -304,9 +304,10 @@ just what it points to). Replace item ~~~~~~~~~~~~ -`HASH_REPLACE` macros are equivalent to HASH_ADD macros except they attempt -to find and delete the item first. If it finds and deletes an item, it will -also return that items pointer as an output parameter. +`HASH_REPLACE` is equivalent to `HASH_ADD`, except that it attempts +to find and delete an equivalent existing item first. If it deletes an +existing item, a pointer to the deleted item will be returned in the +output parameter. Find item @@ -353,11 +354,11 @@ structure we want to remove from the hash. uthash never frees your structure ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Deleting a structure just removes it from the hash table-- it doesn't `free` -it. The choice of when to free your structure is entirely up to you; uthash -will never free your structure. For example when using `HASH_REPLACE` macros, -a replaced output argument is returned back, in order to make it possible for -the user to de-allocate it. +Deleting an item just removes it from the hash table-- it doesn't `free` +it. The choice of when (and how) to deallocate your item structure is +entirely up to you; uthash will never deallocate a pointer it didn't allocate. +For example, `HASH_REPLACE` returns a pointer to the removed item (if any) +in its output parameter, so that the user can deallocate that item. Delete can change the pointer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -723,8 +724,7 @@ struct my_struct { UT_hash_handle hh; /* makes this structure hashable */ }; - -int main(int argc, char *argv[]) { +int main() { const char *names[] = { "joe", "bob", "betty", NULL }; struct my_struct *s, *tmp, *users = NULL; @@ -738,10 +738,19 @@ int main(int argc, char *argv[]) { HASH_FIND_STR(users, "betty", s); if (s) printf("betty's id is %d\n", s->id); - /* free the hash table contents */ + s = (struct my_struct *)malloc(sizeof *s); + strcpy(s->name, "bob"); + s->id = 3; + HASH_REPLACE_STR(users, name, s, tmp); + if (tmp) { + printf("bob's id was %d, but now it's 3\n", tmp->id); + free(tmp); + } + + /* free the hash table's items */ HASH_ITER(hh, users, s, tmp) { - HASH_DEL(users, s); - free(s); + HASH_DEL(users, s); + free(s); } return 0; } @@ -769,8 +778,7 @@ struct my_struct { UT_hash_handle hh; /* makes this structure hashable */ }; - -int main(int argc, char *argv[]) { +int main() { const char *names[] = { "joe", "bob", "betty", NULL }; struct my_struct *s, *tmp, *users = NULL; @@ -784,10 +792,19 @@ int main(int argc, char *argv[]) { HASH_FIND_STR(users, "betty", s); if (s) printf("betty's id is %d\n", s->id); + s = (struct my_struct *)malloc(sizeof *s); + s->name = "bob"; + s->id = 3; + HASH_REPLACE_KEYPTR(hh, users, s->name, strlen(s->name), s, tmp); + if (tmp) { + printf("bob's id was %d, but now it's 3\n", tmp->id); + free(tmp); + } + /* free the hash table contents */ HASH_ITER(hh, users, s, tmp) { - HASH_DEL(users, s); - free(s); + HASH_DEL(users, s); + free(s); } return 0; } @@ -1150,17 +1167,16 @@ always used with the `users_by_name` hash table). Sorted insertion of new items ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you would like to maintain a sorted hash you have two options. The first -option is to use the HASH_SRT() macro, which will sort any unordered list in +To maintain a sorted hash, you have two options. Your first +option is to use the `HASH_SRT` macro, which will sort any unordered list in 'O(n log(n))'. This is the best strategy if you're just filling up a hash -table with items in random order with a single final HASH_SRT() operation -when all is done. Obviously, this won't do what you want if you need -the list to be in an ordered state at times between insertion of -items. You can use HASH_SRT() after every insertion operation, but that will -yield a computational complexity of 'O(n^2 log n)'. - -The second route you can take is via the in-order add and replace macros. -The `HASH_ADD_INORDER*` macros work just like their `HASH_ADD*` counterparts, but +table with items in random order with a single final `HASH_SRT` operation +when all is done. If you need the table to remain sorted as you add and remove +items, you can use `HASH_SRT` after every insertion operation, but that gives +a computational complexity of 'O(n^2 log n)' to insert 'n' items. + +Your second option is to use the in-order add and replace macros. +The `HASH_ADD_*_INORDER` macros work just like their `HASH_ADD_*` counterparts, but with an additional comparison-function argument: int name_sort(struct my_struct *a, struct my_struct *b) { @@ -1169,11 +1185,11 @@ with an additional comparison-function argument: HASH_ADD_KEYPTR_INORDER(hh, items, &item->name, strlen(item->name), item, name_sort); -New items are sorted at insertion time in 'O(n)', thus resulting in a -total computational complexity of 'O(n^2)' for the creation of the hash -table with all items. -For in-order add to work, the list must be in an ordered state before -insertion of the new item. +These macros assume that the hash is already sorted according to the +comparison function, and insert the new item in its proper place. +A single insertion takes 'O(n)', resulting in a total computational +complexity of 'O(n^2)' to insert all 'n' items: slower than a single +`HASH_SRT`, but faster than doing a `HASH_SRT` after every insertion. Several sort orders ~~~~~~~~~~~~~~~~~~~ @@ -1829,39 +1845,43 @@ than `hh`, or if your key's data type isn't `int` or `char[]`. .General macros [width="90%",cols="10m,30m",grid="none",options="header"] |=============================================================================== -|macro | arguments -|HASH_ADD | (hh_name, head, keyfield_name, key_len, item_ptr) -|HASH_ADD_BYHASHVALUE | (hh_name, head, keyfield_name, key_len, hashv, item_ptr) -|HASH_ADD_KEYPTR | (hh_name, head, key_ptr, key_len, item_ptr) -|HASH_ADD_KEYPTR_BYHASHVALUE | (hh_name, head, key_ptr, key_len, hashv, item_ptr) -|HASH_ADD_INORDER | (hh_name, head, keyfield_name, key_len, item_ptr, cmp) -|HASH_ADD_BYHASHVALUE_INORDER | (hh_name, head, keyfield_name, key_len, hashv, item_ptr, cmp) -|HASH_ADD_KEYPTR_INORDER | (hh_name, head, key_ptr, key_len, item_ptr, cmp) -|HASH_ADD_KEYPTR_BYHASHVALUE_INORDER | (hh_name, head, key_ptr, key_len, hashv, item_ptr, cmp) -|HASH_REPLACE | (hh_name, head, keyfield_name, key_len, item_ptr, replaced_item_ptr) -|HASH_REPLACE_BYHASHVALUE | (hh_name, head, keyfield_name, key_len, hashv, item_ptr, replaced_item_ptr) -|HASH_REPLACE_INORDER | (hh_name, head, keyfield_name, key_len, item_ptr, replaced_item_ptr, cmp) -|HASH_REPLACE_BYHASHVALUE_INORDER | (hh_name, head, keyfield_name, key_len, hashv, item_ptr, replaced_item_ptr, cmp) -|HASH_FIND | (hh_name, head, key_ptr, key_len, item_ptr) -|HASH_FIND_BYHASHVALUE | (hh_name, head, key_ptr, key_len, hashv, item_ptr) -|HASH_DELETE | (hh_name, head, item_ptr) -|HASH_VALUE | (key_ptr, key_len, hashv) -|HASH_SRT | (hh_name, head, cmp) -|HASH_CNT | (hh_name, head) -|HASH_CLEAR | (hh_name, head) -|HASH_SELECT | (dst_hh_name, dst_head, src_hh_name, src_head, condition) -|HASH_ITER | (hh_name, head, item_ptr, tmp_item_ptr) -|HASH_OVERHEAD | (hh_name, head) +|macro | arguments +|HASH_ADD | (hh_name, head, keyfield_name, key_len, item_ptr) +|HASH_ADD_INORDER | (hh_name, head, keyfield_name, key_len, item_ptr, cmp) +|HASH_ADD_BYHASHVALUE | (hh_name, head, keyfield_name, key_len, hashv, item_ptr) +|HASH_ADD_BYHASHVALUE_INORDER | (hh_name, head, keyfield_name, key_len, hashv, item_ptr, cmp) +|HASH_ADD_KEYPTR | (hh_name, head, key_ptr, key_len, item_ptr) +|HASH_ADD_KEYPTR_INORDER | (hh_name, head, key_ptr, key_len, item_ptr, cmp) +|HASH_ADD_KEYPTR_BYHASHVALUE | (hh_name, head, key_ptr, key_len, hashv, item_ptr) +|HASH_ADD_KEYPTR_BYHASHVALUE_INORDER | (hh_name, head, key_ptr, key_len, hashv, item_ptr, cmp) +|HASH_REPLACE | (hh_name, head, keyfield_name, key_len, item_ptr, replaced_item_ptr) +|HASH_REPLACE_INORDER | (hh_name, head, keyfield_name, key_len, item_ptr, replaced_item_ptr, cmp) +|HASH_REPLACE_BYHASHVALUE | (hh_name, head, keyfield_name, key_len, hashv, item_ptr, replaced_item_ptr) +|HASH_REPLACE_BYHASHVALUE_INORDER | (hh_name, head, keyfield_name, key_len, hashv, item_ptr, replaced_item_ptr, cmp) +|HASH_REPLACE_KEYPTR | (hh_name, head, key_ptr, key_len, item_ptr, replaced_item_ptr) +|HASH_REPLACE_KEYPTR_INORDER | (hh_name, head, key_ptr, key_len, item_ptr, replaced_item_ptr, cmp) +|HASH_REPLACE_KEYPTR_BYHASHVALUE | (hh_name, head, key_ptr, key_len, hashv, item_ptr, replaced_item_ptr) +|HASH_REPLACE_KEYPTR_BYHASHVALUE_INORDER | (hh_name, head, key_ptr, key_len, hashv, item_ptr, replaced_item_ptr, cmp) +|HASH_FIND | (hh_name, head, key_ptr, key_len, item_ptr) +|HASH_FIND_BYHASHVALUE | (hh_name, head, key_ptr, key_len, hashv, item_ptr) +|HASH_DELETE | (hh_name, head, item_ptr) +|HASH_VALUE | (key_ptr, key_len, hashv) +|HASH_SRT | (hh_name, head, cmp) +|HASH_CNT | (hh_name, head) +|HASH_CLEAR | (hh_name, head) +|HASH_SELECT | (dst_hh_name, dst_head, src_hh_name, src_head, condition) +|HASH_ITER | (hh_name, head, item_ptr, tmp_item_ptr) +|HASH_OVERHEAD | (hh_name, head) |=============================================================================== [NOTE] `HASH_ADD_KEYPTR` is used when the structure contains a pointer to the key, rather than the key itself. -The `HASH_VALUE` and `..._BYHASHVALUE` macros are a performance mechanism mainly for the +The `HASH_VALUE` and `*_BYHASHVALUE` macros are a performance mechanism mainly for the special case of having different structures, in different hash tables, having identical keys. It allows the hash value to be obtained once and then passed -in to the `..._BYHASHVALUE` macros, saving the expense of re-computing the hash value. +in to the `*_BYHASHVALUE` macros, saving the expense of re-computing the hash value. Argument descriptions @@ -1887,7 +1907,7 @@ key_ptr:: `HASH_ADD_KEYPTR`, this is the address of the key of the item being added. hashv:: the hash value of the provided key. This is an input parameter for the - `..._BYHASHVALUE` macros, and an output parameter for `HASH_VALUE`. + `*_BYHASHVALUE` macros, and an output parameter for `HASH_VALUE`. Reusing a cached hash value can be a performance optimization if you're going to do repeated lookups for the same key. item_ptr:: diff --git a/src/uthash.h b/src/uthash.h index 68693bf..963422b 100644 --- a/src/uthash.h +++ b/src/uthash.h @@ -138,41 +138,24 @@ do { /* calculate the hash handle from element address elp */ #define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho))) -#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ -do { \ - struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ - unsigned _hd_bkt; \ - HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ - (head)->hh.tbl->buckets[_hd_bkt].count++; \ - _hd_hh_item->hh_next = NULL; \ - _hd_hh_item->hh_prev = NULL; \ -} while (0) - #define HASH_VALUE(keyptr,keylen,hashv) \ do { \ HASH_FUNCTION(keyptr, keylen, hashv); \ } while (0) -#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ +#define HASH_TO_BKT(hashv,num_bkts,bkt) \ do { \ - (out) = NULL; \ - if (head) { \ - unsigned _hf_bkt; \ - HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ - if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ - HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ - } \ - } \ + bkt = ((hashv) & ((num_bkts) - 1U)); \ } while (0) -#define HASH_FIND(hh,head,keyptr,keylen,out) \ +#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ do { \ - (out) = NULL; \ - if (head) { \ - unsigned _hf_hashv; \ - HASH_VALUE(keyptr, keylen, _hf_hashv); \ - HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ - } \ + struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ + unsigned _hd_bkt; \ + HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + (head)->hh.tbl->buckets[_hd_bkt].count++; \ + _hd_hh_item->hh_next = NULL; \ + _hd_hh_item->hh_prev = NULL; \ } while (0) #ifdef HASH_BLOOM @@ -244,40 +227,6 @@ do { } \ } while (0) -#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ -do { \ - (replaced) = NULL; \ - HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ - if (replaced) { \ - HASH_DELETE(hh, head, replaced); \ - } \ - HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ -} while (0) - -#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ -do { \ - (replaced) = NULL; \ - HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ - if (replaced) { \ - HASH_DELETE(hh, head, replaced); \ - } \ - HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ -} while (0) - -#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ -do { \ - unsigned _hr_hashv; \ - HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ - HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ -} while (0) - -#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ -do { \ - unsigned _hr_hashv; \ - HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ - HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ -} while (0) - #define HASH_APPEND_LIST(hh, head, add) \ do { \ (add)->hh.next = NULL; \ @@ -349,6 +298,74 @@ do { #endif +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_hashv; \ + HASH_VALUE(keyptr, keylen, _hf_hashv); \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ + } \ +} while (0) + +#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_bkt; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ + } \ + } \ +} while (0) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) + +#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ + HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) + +#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) + +#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_hashv; \ + HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ +} while (0) + +#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ +do { \ + unsigned _hs_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ +} while (0) + +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (const void*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ +} while (0) #define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ do { \ @@ -383,56 +400,50 @@ do { HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ } while (0) -#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ -do { \ - unsigned _hs_hashv; \ - HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ - HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ -} while (0) +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ + HASH_REPLACE_KEYPTR(hh, head, &(add)->fieldname, keylen_in, add, replaced) -#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ - HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) +#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ + HASH_REPLACE_KEYPTR_INORDER(hh, head, &(add)->fieldname, keylen_in, add, replaced, cmpfcn) -#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ - HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) +#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ + HASH_REPLACE_KEYPTR_BYHASHVALUE(hh, head, &(add)->fieldname, keylen_in, hashval, add, replaced) -#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ + HASH_REPLACE_KEYPTR_BYHASHVALUE_INORDER(hh, head, &(add)->fieldname, keylen_in, hashval, add, replaced, cmpfcn) + +#define HASH_REPLACE_KEYPTR(hh,head,keyptr,keylen_in,add,replaced) \ do { \ - IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ - (add)->hh.hashv = (hashval); \ - (add)->hh.key = (const void*) (keyptr); \ - (add)->hh.keylen = (unsigned) (keylen_in); \ - if (!(head)) { \ - (add)->hh.next = NULL; \ - (add)->hh.prev = NULL; \ - HASH_MAKE_TABLE(hh, add, _ha_oomed); \ - IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ - (head) = (add); \ - IF_HASH_NONFATAL_OOM( } ) \ - } else { \ - (add)->hh.tbl = (head)->hh.tbl; \ - HASH_APPEND_LIST(hh, head, add); \ - } \ - HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ - HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ + unsigned _hr_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hr_hashv); \ + HASH_REPLACE_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _hr_hashv, add, replaced); \ } while (0) -#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +#define HASH_REPLACE_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,replaced,cmpfcn) \ do { \ - unsigned _ha_hashv; \ - HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ - HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ + unsigned _hr_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hr_hashv); \ + HASH_REPLACE_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ } while (0) -#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ - HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) - -#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ - HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) +#define HASH_REPLACE_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add,replaced) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, hashval, add); \ +} while (0) -#define HASH_TO_BKT(hashv,num_bkts,bkt) \ +#define HASH_REPLACE_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,replaced,cmpfcn) \ do { \ - bkt = ((hashv) & ((num_bkts) - 1U)); \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, hashval, add, cmpfcn); \ } while (0) /* delete "delptr" from the hash table. diff --git a/tests/test15.ans b/tests/test15.ans index ad69a94..2c385dd 100644 --- a/tests/test15.ans +++ b/tests/test15.ans @@ -1 +1,2 @@ betty's id is 2 +bob's id was 1, but now it's 3 diff --git a/tests/test15.c b/tests/test15.c index 5614092..62d2ed1 100644 --- a/tests/test15.c +++ b/tests/test15.c @@ -4,34 +4,35 @@ #include "uthash.h" struct my_struct { - char name[10]; /* key */ + char name[10]; /* key (string is WITHIN the structure) */ int id; UT_hash_handle hh; /* makes this structure hashable */ }; - -int main() -{ - const char **n, *names[] = { "joe", "bob", "betty", NULL }; +int main() { + const char *names[] = { "joe", "bob", "betty", NULL }; struct my_struct *s, *tmp, *users = NULL; - int i=0; - for (n = names; *n != NULL; n++) { - s = (struct my_struct*)malloc(sizeof(struct my_struct)); - if (s == NULL) { - exit(-1); - } - strcpy(s->name, *n); - s->id = i++; - HASH_ADD_STR( users, name, s ); + for (int i = 0; names[i]; ++i) { + s = (struct my_struct *)malloc(sizeof *s); + strcpy(s->name, names[i]); + s->id = i; + HASH_ADD_STR(users, name, s); } - HASH_FIND_STR( users, "betty", s); - if (s != NULL) { - printf("betty's id is %d\n", s->id); + HASH_FIND_STR(users, "betty", s); + if (s) printf("betty's id is %d\n", s->id); + + s = (struct my_struct *)malloc(sizeof *s); + strcpy(s->name, "bob"); + s->id = 3; + HASH_REPLACE_STR(users, name, s, tmp); + if (tmp) { + printf("bob's id was %d, but now it's 3\n", tmp->id); + free(tmp); } - /* free the hash table contents */ + /* free the hash table's items */ HASH_ITER(hh, users, s, tmp) { HASH_DEL(users, s); free(s); diff --git a/tests/test40.ans b/tests/test40.ans index ad69a94..2c385dd 100644 --- a/tests/test40.ans +++ b/tests/test40.ans @@ -1 +1,2 @@ betty's id is 2 +bob's id was 1, but now it's 3 diff --git a/tests/test40.c b/tests/test40.c index af75461..77eb0fb 100644 --- a/tests/test40.c +++ b/tests/test40.c @@ -9,26 +9,27 @@ struct my_struct { UT_hash_handle hh; /* makes this structure hashable */ }; - -int main() -{ - const char **n, *names[] = { "joe", "bob", "betty", NULL }; +int main() { + const char *names[] = { "joe", "bob", "betty", NULL }; struct my_struct *s, *tmp, *users = NULL; - int i=0; - for (n = names; *n != NULL; n++) { - s = (struct my_struct*)malloc(sizeof(struct my_struct)); - if (s == NULL) { - exit(-1); - } - s->name = *n; - s->id = i++; - HASH_ADD_KEYPTR( hh, users, s->name, strlen(s->name), s ); + for (int i = 0; names[i]; ++i) { + s = (struct my_struct *)malloc(sizeof *s); + s->name = names[i]; + s->id = i; + HASH_ADD_KEYPTR(hh, users, s->name, strlen(s->name), s); } - HASH_FIND_STR( users, "betty", s); - if (s != NULL) { - printf("betty's id is %d\n", s->id); + HASH_FIND_STR(users, "betty", s); + if (s) printf("betty's id is %d\n", s->id); + + s = (struct my_struct *)malloc(sizeof *s); + s->name = "bob"; + s->id = 3; + HASH_REPLACE_KEYPTR(hh, users, s->name, strlen(s->name), s, tmp); + if (tmp) { + printf("bob's id was %d, but now it's 3\n", tmp->id); + free(tmp); } /* free the hash table contents */ diff --git a/tests/test87.ans b/tests/test87.ans index 32fc95e..7f2fea0 100644 --- a/tests/test87.ans +++ b/tests/test87.ans @@ -40,39 +40,65 @@ 2: muh1 3: muh5 5: muh6 +6: muh7 6: muh9 7: muh12 8: muh2 -8: muh4 9: muh10 9: muh3 10: muh11 15: muh8 -16: muh7 +18: muh4 ### 2: muh1 3: muh5 5: muh6 6: muh9 7: muh12 -8: muh4 +8: muh2 9: muh10 9: muh3 10: muh11 15: muh8 16: muh7 +18: muh4 ### 2: muh1 3: muh5 5: muh6 +5: muh8 +6: muh9 +7: muh12 +8: muh2 +9: muh10 +9: muh3 +10: muh11 +16: muh7 +18: muh4 +### +2: muh1 +3: muh5 +5: muh6 +5: muh8 +6: muh9 +7: muh12 +9: muh10 +9: muh3 +10: muh11 +16: muh7 +18: muh4 +### +2: muh1 +3: muh5 +5: muh6 +5: muh8 6: muh9 7: muh12 -8: muh4 8: muh2 9: muh10 9: muh3 10: muh11 -15: muh8 16: muh7 +18: muh4 ### ### diff --git a/tests/test87.c b/tests/test87.c index c17275e..8048471 100644 --- a/tests/test87.c +++ b/tests/test87.c @@ -55,13 +55,23 @@ int main() }; int index; - for (index = 0; index < 11; ++index) { - HASH_ADD_INORDER(hh, hTable, name[0], strlen(tst[index].name), &tst[index], CMPFUNC); - } - // test HASH_ADD_BYHASHVALUE_INORDER - HASH_VALUE(tst[11].name, strlen(tst[11].name), hashvalue); - HASH_ADD_BYHASHVALUE_INORDER(hh, hTable, name[0], strlen(tst[11].name), hashvalue, &tst[11], CMPFUNC); + HASH_ADD_INORDER(hh, hTable, name, strlen(tst[0].name), &tst[0], CMPFUNC); + HASH_ADD_INORDER(hh, hTable, name[0], strlen(tst[1].name), &tst[1], CMPFUNC); + HASH_ADD_KEYPTR_INORDER(hh, hTable, tst[2].name, strlen(tst[2].name), &tst[2], CMPFUNC); + HASH_ADD_KEYPTR_INORDER(hh, hTable, &tst[3].name[0], strlen(tst[3].name), &tst[3], CMPFUNC); + HASH_VALUE(tst[4].name, strlen(tst[4].name), hashvalue); + HASH_ADD_BYHASHVALUE_INORDER(hh, hTable, name, strlen(tst[4].name), hashvalue, &tst[4], CMPFUNC); + HASH_VALUE(tst[5].name, strlen(tst[5].name), hashvalue); + HASH_ADD_BYHASHVALUE_INORDER(hh, hTable, name[0], strlen(tst[5].name), hashvalue, &tst[5], CMPFUNC); + HASH_VALUE(tst[6].name, strlen(tst[6].name), hashvalue); + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, hTable, tst[6].name, strlen(tst[6].name), hashvalue, &tst[6], CMPFUNC); + HASH_VALUE(tst[7].name, strlen(tst[7].name), hashvalue); + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, hTable, &tst[7].name[0], strlen(tst[7].name), hashvalue, &tst[7], CMPFUNC); + + for (index = 8; index < 12; ++index) { + HASH_ADD_INORDER(hh, hTable, name, strlen(tst[index].name), &tst[index], CMPFUNC); + } printtable(hTable); @@ -78,6 +88,13 @@ int main() printtable(hTable); + // rehash "8: muh4" to "18: muh4" + tst[3].weight = 18; + HASH_REPLACE_KEYPTR_INORDER(hh, hTable, &tst[3].name, strlen(tst[3].name), &tst[3], replaced, CMPFUNC); + assert(replaced == &tst[3]); + + printtable(hTable); + // rehash "6: muh7" to "16: muh7" tst[6].weight = 16; HASH_VALUE(&tst[6].name[0], strlen(tst[6].name), hashvalue); @@ -86,6 +103,14 @@ int main() printtable(hTable); + // rehash "15: muh8" to "5: muh8" + tst[7].weight = 5; + HASH_VALUE(&tst[7].name[0], strlen(tst[7].name), hashvalue); + HASH_REPLACE_KEYPTR_BYHASHVALUE_INORDER(hh, hTable, &tst[7].name, strlen(tst[7].name), hashvalue, &tst[7], replaced, CMPFUNC); + assert(replaced == &tst[7]); + + printtable(hTable); + // remove "8: muh2"... HASH_DELETE(hh, hTable, &tst[1]); diff --git a/tests/test90.ans b/tests/test90.ans index e9d17d7..6f27ea3 100644 --- a/tests/test90.ans +++ b/tests/test90.ans @@ -1,2 +1,3 @@ filling in is ok -cleanup is ok +replacing is ok +4950 diff --git a/tests/test90.c b/tests/test90.c index 6fce121..ae7dce0 100644 --- a/tests/test90.c +++ b/tests/test90.c @@ -4,7 +4,7 @@ #include "uthash.h" struct item { - unsigned char *sort_field; + void *sort_field; size_t sort_field_len; /** Sort field length, in bytes */ int some_user_data; UT_hash_handle hh; @@ -12,42 +12,60 @@ struct item { int sort_func(const struct item *a, const struct item *b) { - int va = *(int*)(void*)a->sort_field; - int vb = *(int*)(void*)b->sort_field; + int va = *(int*)a->sort_field; + int vb = *(int*)b->sort_field; return (va < vb) ? -1 : (va > vb); } int main() { - size_t i; + int i; struct item *p, *tmp; int total = 0; /** The sorted list */ struct item *list = NULL; - int counter = 0; /* fill in the sorted list */ - for(i=0; i<100; i++) { + for (i = 0; i < 100; i += 2) { p = (struct item *)malloc(sizeof *p); p->sort_field_len = sizeof(int); - p->sort_field = (unsigned char *)malloc(p->sort_field_len); - *(int*)(void*)p->sort_field = counter++; + p->sort_field = malloc(p->sort_field_len); + *(int*)p->sort_field = i; HASH_ADD_KEYPTR_INORDER(hh, list, p->sort_field, p->sort_field_len, p, sort_func); } printf("filling in is ok\n"); + for (i = 0; i < 100; ++i) { + p = (struct item *)malloc(sizeof *p); + + p->sort_field_len = sizeof(int); + p->sort_field = malloc(p->sort_field_len); + *(int*)p->sort_field = i; + + HASH_REPLACE_KEYPTR_INORDER(hh, list, p->sort_field, p->sort_field_len, p, tmp, sort_func); + if (tmp != NULL) { + free(tmp->sort_field); + free(tmp); + } + } + + printf("replacing is ok\n"); + + i = 0; HASH_ITER(hh, list, p, tmp) { - total += *(int*)(void*)p->sort_field; + int value = *(int*)(void*)p->sort_field; + assert(i == value); + total += value; HASH_DEL(list, p); free(p->sort_field); free(p); + i += 1; } - assert(total == 4950); // sum of 0 through 99 + printf("%d\n", total); - printf("cleanup is ok\n"); return 0; }