diff options
Diffstat (limited to 'doc/userguide.txt')
-rw-r--r-- | doc/userguide.txt | 222 |
1 files changed, 118 insertions, 104 deletions
diff --git a/doc/userguide.txt b/doc/userguide.txt index 4d8ce8f0a..39fa7d1ed 100644 --- a/doc/userguide.txt +++ b/doc/userguide.txt @@ -1,7 +1,7 @@ uthash User Guide ================= Troy D. Hanson, Arthur O'Dwyer -v2.1.0, December 2018 +v2.3.0, February 2021 To download uthash, follow this link back to the https://github.com/troydhanson/uthash[GitHub project page]. @@ -215,10 +215,10 @@ a unique value. Then call `HASH_ADD`. (Here we use the convenience macro void add_user(int user_id, char *name) { struct my_struct *s; - s = malloc(sizeof(struct my_struct)); + s = malloc(sizeof *s); s->id = user_id; strcpy(s->name, name); - HASH_ADD_INT( users, id, s ); /* id: name of key field */ + HASH_ADD_INT(users, id, s); /* id: name of key field */ } ---------------------------------------------------------------------- @@ -227,7 +227,7 @@ second parameter is the 'name' of the key field. Here, this is `id`. The last parameter is a pointer to the structure being added. [[validc]] -.Wait.. the field name is a parameter? +.Wait.. the parameter is a field name? ******************************************************************************* If you find it strange that `id`, which is the 'name of a field' in the structure, can be passed as a parameter... welcome to the world of macros. Don't @@ -256,10 +256,10 @@ Otherwise we just modify the structure that already exists. struct my_struct *s; HASH_FIND_INT(users, &user_id, s); /* id already in the hash? */ - if (s==NULL) { + if (s == NULL) { s = (struct my_struct *)malloc(sizeof *s); s->id = user_id; - HASH_ADD_INT( users, id, s ); /* id: name of key field */ + HASH_ADD_INT(users, id, s); /* id: name of key field */ } strcpy(s->name, name); } @@ -284,7 +284,7 @@ right. /* bad */ void add_user(struct my_struct *users, int user_id, char *name) { ... - HASH_ADD_INT( users, id, s ); + HASH_ADD_INT(users, id, s); } You really need to pass 'a pointer' to the hash pointer: @@ -292,7 +292,7 @@ You really need to pass 'a pointer' to the hash pointer: /* good */ void add_user(struct my_struct **users, int user_id, char *name) { ... ... - HASH_ADD_INT( *users, id, s ); + HASH_ADD_INT(*users, id, s); } Note that we dereferenced the pointer in the `HASH_ADD` also. @@ -319,7 +319,7 @@ To look up a structure in a hash, you need its key. Then call `HASH_FIND`. struct my_struct *find_user(int user_id) { struct my_struct *s; - HASH_FIND_INT( users, &user_id, s ); /* s: output pointer */ + HASH_FIND_INT(users, &user_id, s); /* s: output pointer */ return s; } ---------------------------------------------------------------------- @@ -376,8 +376,8 @@ void delete_all() { struct my_struct *current_user, *tmp; HASH_ITER(hh, users, current_user, tmp) { - HASH_DEL(users,current_user); /* delete; users advances to next */ - free(current_user); /* optional- if you want to free */ + HASH_DEL(users, current_user); /* delete; users advances to next */ + free(current_user); /* optional- if you want to free */ } } ---------------------------------------------------------------------- @@ -387,7 +387,7 @@ All-at-once deletion If you only want to delete all the items, but not free them or do any per-element clean up, you can do this more efficiently in a single operation: - HASH_CLEAR(hh,users); + HASH_CLEAR(hh, users); Afterward, the list head (here, `users`) will be set to `NULL`. @@ -403,7 +403,7 @@ num_users = HASH_COUNT(users); printf("there are %u users\n", num_users); ---------------------------------------------------------------------- -Incidentally, this works even the list (`users`, here) is `NULL`, in +Incidentally, this works even if the list head (here, `users`) is `NULL`, in which case the count is 0. Iterating and sorting @@ -417,7 +417,7 @@ following the `hh.next` pointer. void print_users() { struct my_struct *s; - for(s=users; s != NULL; s=s->hh.next) { + for (s = users; s != NULL; s = s->hh.next) { printf("user id %d: name %s\n", s->id, s->name); } } @@ -430,7 +430,7 @@ the hash, starting from any known item. Deletion-safe iteration ^^^^^^^^^^^^^^^^^^^^^^^ In the example above, it would not be safe to delete and free `s` in the body -of the 'for' loop, (because `s` is derefenced each time the loop iterates). +of the 'for' loop, (because `s` is dereferenced each time the loop iterates). This is easy to rewrite correctly (by copying the `s->hh.next` pointer to a temporary variable 'before' freeing `s`), but it comes up often enough that a deletion-safe iteration macro, `HASH_ITER`, is included. It expands to a @@ -452,14 +452,14 @@ doubly-linked list. ******************************************************************************* If you're using uthash in a C++ program, you need an extra cast on the `for` -iterator, e.g., `s=(struct my_struct*)s->hh.next`. +iterator, e.g., `s = static_cast<my_struct*>(s->hh.next)`. Sorting ^^^^^^^ The items in the hash are visited in "insertion order" when you follow the `hh.next` pointer. You can sort the items into a new order using `HASH_SORT`. - HASH_SORT( users, name_sort ); + HASH_SORT(users, name_sort); The second argument is a pointer to a comparison function. It must accept two pointer arguments (the items to compare), and must return an `int` which is @@ -479,20 +479,20 @@ Below, `name_sort` and `id_sort` are two examples of sort functions. .Sorting the items in the hash ---------------------------------------------------------------------- -int name_sort(struct my_struct *a, struct my_struct *b) { - return strcmp(a->name,b->name); +int by_name(const struct my_struct *a, const struct my_struct *b) { + return strcmp(a->name, b->name); } -int id_sort(struct my_struct *a, struct my_struct *b) { +int by_id(const struct my_struct *a, const struct my_struct *b) { return (a->id - b->id); } void sort_by_name() { - HASH_SORT(users, name_sort); + HASH_SORT(users, by_name); } void sort_by_id() { - HASH_SORT(users, id_sort); + HASH_SORT(users, by_id); } ---------------------------------------------------------------------- @@ -516,85 +516,100 @@ Follow the prompts to try the program. .A complete program ---------------------------------------------------------------------- -#include <stdio.h> /* gets */ +#include <stdio.h> /* printf */ #include <stdlib.h> /* atoi, malloc */ #include <string.h> /* strcpy */ #include "uthash.h" struct my_struct { int id; /* key */ - char name[10]; + char name[21]; UT_hash_handle hh; /* makes this structure hashable */ }; struct my_struct *users = NULL; -void add_user(int user_id, char *name) { +void add_user(int user_id, const char *name) +{ struct my_struct *s; HASH_FIND_INT(users, &user_id, s); /* id already in the hash? */ - if (s==NULL) { - s = (struct my_struct *)malloc(sizeof *s); - s->id = user_id; - HASH_ADD_INT( users, id, s ); /* id: name of key field */ + if (s == NULL) { + s = (struct my_struct*)malloc(sizeof *s); + s->id = user_id; + HASH_ADD_INT(users, id, s); /* id is the key field */ } strcpy(s->name, name); } -struct my_struct *find_user(int user_id) { +struct my_struct *find_user(int user_id) +{ struct my_struct *s; - HASH_FIND_INT( users, &user_id, s ); /* s: output pointer */ + HASH_FIND_INT(users, &user_id, s); /* s: output pointer */ return s; } -void delete_user(struct my_struct *user) { +void delete_user(struct my_struct *user) +{ HASH_DEL(users, user); /* user: pointer to deletee */ free(user); } -void delete_all() { - struct my_struct *current_user, *tmp; +void delete_all() +{ + struct my_struct *current_user; + struct my_struct *tmp; - HASH_ITER(hh, users, current_user, tmp) { - HASH_DEL(users, current_user); /* delete it (users advances to next) */ - free(current_user); /* free it */ - } + HASH_ITER(hh, users, current_user, tmp) { + HASH_DEL(users, current_user); /* delete it (users advances to next) */ + free(current_user); /* free it */ + } } -void print_users() { +void print_users() +{ struct my_struct *s; - for(s=users; s != NULL; s=(struct my_struct*)(s->hh.next)) { + for (s = users; s != NULL; s = (struct my_struct*)(s->hh.next)) { printf("user id %d: name %s\n", s->id, s->name); } } -int name_sort(struct my_struct *a, struct my_struct *b) { - return strcmp(a->name,b->name); +int by_name(const struct my_struct *a, const struct my_struct *b) +{ + return strcmp(a->name, b->name); } -int id_sort(struct my_struct *a, struct my_struct *b) { +int by_id(const struct my_struct *a, const struct my_struct *b) +{ return (a->id - b->id); } -void sort_by_name() { - HASH_SORT(users, name_sort); -} - -void sort_by_id() { - HASH_SORT(users, id_sort); +const char *getl(const char *prompt) +{ + static char buf[21]; + char *p; + printf("%s? ", prompt); fflush(stdout); + p = fgets(buf, sizeof(buf), stdin); + if (p == NULL || (p = strchr(buf, '\n')) == NULL) { + puts("Invalid input!"); + exit(EXIT_FAILURE); + } + *p = '\0'; + return buf; } -int main(int argc, char *argv[]) { - char in[10]; - int id=1, running=1; +int main() +{ + int id = 1; + int running = 1; struct my_struct *s; - unsigned num_users; + int temp; while (running) { printf(" 1. add user\n"); - printf(" 2. add/rename user by id\n"); + printf(" 2. add or rename user by id\n"); printf(" 3. find user\n"); printf(" 4. delete user\n"); printf(" 5. delete all users\n"); @@ -603,47 +618,44 @@ int main(int argc, char *argv[]) { printf(" 8. print users\n"); printf(" 9. count users\n"); printf("10. quit\n"); - gets(in); - switch(atoi(in)) { + switch (atoi(getl("Command"))) { case 1: - printf("name?\n"); - add_user(id++, gets(in)); + add_user(id++, getl("Name (20 char max)")); break; case 2: - printf("id?\n"); - gets(in); id = atoi(in); - printf("name?\n"); - add_user(id, gets(in)); + temp = atoi(getl("ID")); + add_user(temp, getl("Name (20 char max)")); break; case 3: - printf("id?\n"); - s = find_user(atoi(gets(in))); + s = find_user(atoi(getl("ID to find"))); printf("user: %s\n", s ? s->name : "unknown"); break; case 4: - printf("id?\n"); - s = find_user(atoi(gets(in))); - if (s) delete_user(s); - else printf("id unknown\n"); + s = find_user(atoi(getl("ID to delete"))); + if (s) { + delete_user(s); + } else { + printf("id unknown\n"); + } break; case 5: delete_all(); break; case 6: - sort_by_name(); + HASH_SORT(users, by_name); break; case 7: - sort_by_id(); + HASH_SORT(users, by_id); break; case 8: print_users(); break; case 9: - num_users=HASH_COUNT(users); - printf("there are %u users\n", num_users); + temp = HASH_COUNT(users); + printf("there are %d users\n", temp); break; case 10: - running=0; + running = 0; break; } } @@ -720,10 +732,10 @@ int main(int argc, char *argv[]) { s = (struct my_struct *)malloc(sizeof *s); strcpy(s->name, names[i]); s->id = i; - HASH_ADD_STR( users, name, s ); + HASH_ADD_STR(users, name, s); } - HASH_FIND_STR( users, "betty", s); + HASH_FIND_STR(users, "betty", s); if (s) printf("betty's id is %d\n", s->id); /* free the hash table contents */ @@ -766,10 +778,10 @@ int main(int argc, char *argv[]) { 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_ADD_KEYPTR(hh, users, s->name, strlen(s->name), s); } - HASH_FIND_STR( users, "betty", s); + HASH_FIND_STR(users, "betty", s); if (s) printf("betty's id is %d\n", s->id); /* free the hash table contents */ @@ -812,12 +824,12 @@ int main() { if (!e) return -1; e->key = (void*)someaddr; e->i = 1; - HASH_ADD_PTR(hash,key,e); + HASH_ADD_PTR(hash, key, e); HASH_FIND_PTR(hash, &someaddr, d); if (d) printf("found\n"); /* release memory */ - HASH_DEL(hash,e); + HASH_DEL(hash, e); free(e); return 0; } @@ -924,7 +936,7 @@ int main(int argc, char *argv[]) { int beijing[] = {0x5317, 0x4eac}; /* UTF-32LE for 北京 */ /* allocate and initialize our structure */ - msg = (msg_t *)malloc( sizeof(msg_t) + sizeof(beijing) ); + msg = (msg_t *)malloc(sizeof(msg_t) + sizeof(beijing)); memset(msg, 0, sizeof(msg_t)+sizeof(beijing)); /* zero fill */ msg->len = sizeof(beijing); msg->encoding = UTF32; @@ -936,16 +948,16 @@ int main(int argc, char *argv[]) { - offsetof(msg_t, encoding); /* offset of first key field */ /* add our structure to the hash table */ - HASH_ADD( hh, msgs, encoding, keylen, msg); + HASH_ADD(hh, msgs, encoding, keylen, msg); /* look it up to prove that it worked :-) */ - msg=NULL; + msg = NULL; lookup_key = (lookup_key_t *)malloc(sizeof(*lookup_key) + sizeof(beijing)); memset(lookup_key, 0, sizeof(*lookup_key) + sizeof(beijing)); lookup_key->encoding = UTF32; memcpy(lookup_key->text, beijing, sizeof(beijing)); - HASH_FIND( hh, msgs, &lookup_key->encoding, keylen, msg ); + HASH_FIND(hh, msgs, &lookup_key->encoding, keylen, msg); if (msg) printf("found \n"); free(lookup_key); @@ -1028,7 +1040,7 @@ typedef struct item { UT_hash_handle hh; } item_t; -item_t *items=NULL; +item_t *items = NULL; int main(int argc, char *argvp[]) { item_t *item1, *item2, *tmp1, *tmp2; @@ -1119,7 +1131,7 @@ always used with the `users_by_name` hash table). int i; char *name; - s = malloc(sizeof(struct my_struct)); + s = malloc(sizeof *s); s->id = 1; strcpy(s->username, "thanson"); @@ -1128,7 +1140,7 @@ always used with the `users_by_name` hash table). HASH_ADD(hh2, users_by_name, username, strlen(s->username), s); /* find user by ID in the "users_by_id" hash table */ - i=1; + i = 1; HASH_FIND(hh1, users_by_id, &i, sizeof(int), s); if (s) printf("found id %d: %s\n", i, s->username); @@ -1155,7 +1167,7 @@ The `HASH_ADD_INORDER*` macros work just like their `HASH_ADD*` counterparts, bu with an additional comparison-function argument: int name_sort(struct my_struct *a, struct my_struct *b) { - return strcmp(a->name,b->name); + return strcmp(a->name, b->name); } HASH_ADD_KEYPTR_INORDER(hh, items, &item->name, strlen(item->name), item, name_sort); @@ -1183,7 +1195,7 @@ Now we can define two sort functions, then use `HASH_SRT`. } int sort_by_name(struct my_struct *a, struct my_struct *b) { - return strcmp(a->username,b->username); + return strcmp(a->username, b->username); } HASH_SRT(hh1, users_by_id, sort_by_id); @@ -1240,7 +1252,8 @@ for a structure to be usable with `HASH_SELECT`, it must have two or more hash handles. (As described <<multihash,here>>, a structure can exist in many hash tables at the same time; it must have a separate hash handle for each one). - user_t *users=NULL, *admins=NULL; /* two hash tables */ + user_t *users = NULL; /* hash table of users */ + user_t *admins = NULL; /* hash table of admins */ typedef struct { int id; @@ -1252,25 +1265,26 @@ Now suppose we have added some users, and want to select just the administrator users who have id's less than 1024. #define is_admin(x) (((user_t*)x)->id < 1024) - HASH_SELECT(ah,admins,hh,users,is_admin); + HASH_SELECT(ah, admins, hh, users, is_admin); The first two parameters are the 'destination' hash handle and hash table, the second two parameters are the 'source' hash handle and hash table, and the last -parameter is the 'select condition'. Here we used a macro `is_admin()` but we +parameter is the 'select condition'. Here we used a macro `is_admin(x)` but we could just as well have used a function. - int is_admin(void *userv) { - user_t *user = (user_t*)userv; + int is_admin(const void *userv) { + user_t *user = (const user_t*)userv; return (user->id < 1024) ? 1 : 0; } If the select condition always evaluates to true, this operation is -essentially a 'merge' of the source hash into the destination hash. Of course, -the source hash remains unchanged under any use of `HASH_SELECT`. It only adds -items to the destination hash selectively. +essentially a 'merge' of the source hash into the destination hash. + +`HASH_SELECT` adds items to the destination without removing them from +the source; the source hash table remains unchanged. The destination hash table +must not be the same as the source hash table. -The two hash handles must differ. An example of using `HASH_SELECT` is included -in `tests/test36.c`. +An example of using `HASH_SELECT` is included in `tests/test36.c`. [[hash_keycompare]] Specifying an alternate key comparison function @@ -1290,7 +1304,7 @@ that do not provide `memcmp`, you can substitute your own implementation. ---------------------------------------------------------------------------- #undef HASH_KEYCMP -#define HASH_KEYCMP(a,b,len) bcmp(a,b,len) +#define HASH_KEYCMP(a,b,len) bcmp(a, b, len) ---------------------------------------------------------------------------- Another reason to substitute your own key comparison function is if your "key" is not @@ -1631,7 +1645,7 @@ If your application uses its own custom allocator, uthash can use them too. /* re-define, specifying alternate functions */ #define uthash_malloc(sz) my_malloc(sz) -#define uthash_free(ptr,sz) my_free(ptr) +#define uthash_free(ptr, sz) my_free(ptr) ... ---------------------------------------------------------------------------- @@ -1647,7 +1661,7 @@ provide these functions, you can substitute your own implementations. ---------------------------------------------------------------------------- #undef uthash_bzero -#define uthash_bzero(a,len) my_bzero(a,len) +#define uthash_bzero(a, len) my_bzero(a, len) #undef uthash_strlen #define uthash_strlen(s) my_strlen(s) @@ -1754,7 +1768,7 @@ concurrent readers (since uthash 1.5). For example using pthreads you can create an rwlock like this: pthread_rwlock_t lock; - if (pthread_rwlock_init(&lock,NULL) != 0) fatal("can't create rwlock"); + if (pthread_rwlock_init(&lock, NULL) != 0) fatal("can't create rwlock"); Then, readers must acquire the read lock before doing any `HASH_FIND` calls or before iterating over the hash elements: @@ -1795,10 +1809,10 @@ In order to use the convenience macros, |=============================================================================== |macro | arguments |HASH_ADD_INT | (head, keyfield_name, item_ptr) -|HASH_REPLACE_INT | (head, keyfiled_name, item_ptr,replaced_item_ptr) +|HASH_REPLACE_INT | (head, keyfield_name, item_ptr, replaced_item_ptr) |HASH_FIND_INT | (head, key_ptr, item_ptr) |HASH_ADD_STR | (head, keyfield_name, item_ptr) -|HASH_REPLACE_STR | (head,keyfield_name, item_ptr, replaced_item_ptr) +|HASH_REPLACE_STR | (head, keyfield_name, item_ptr, replaced_item_ptr) |HASH_FIND_STR | (head, key_ptr, item_ptr) |HASH_ADD_PTR | (head, keyfield_name, item_ptr) |HASH_REPLACE_PTR | (head, keyfield_name, item_ptr, replaced_item_ptr) |