Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 79 additions & 59 deletions doc/userguide.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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
Expand Down Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -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;

Expand All @@ -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;
}
Expand Down Expand Up @@ -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;

Expand All @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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
~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -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
Expand All @@ -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::
Expand Down
Loading