|
- #include <stdio.h>
-
- #define HASH_NONFATAL_OOM 1
-
- #include "uthash.h"
-
- #undef uthash_malloc
- #undef uthash_free
- #undef uthash_nonfatal_oom
- #define uthash_malloc(sz) alt_malloc(sz)
- #define uthash_free(ptr,sz) alt_free(ptr)
- #define uthash_nonfatal_oom(e) do{(e)->mem_failed=1;}while(0)
- #define all_select(a) 1
-
- typedef struct example_user_t {
- int id;
- int cookie;
- UT_hash_handle hh;
- UT_hash_handle hh2;
- int mem_failed;
- } example_user_t;
-
- static int malloc_cnt = 0;
- static int malloc_failed = 0;
- static int free_cnt = 0;
-
- static void *alt_malloc(size_t sz)
- {
- if (--malloc_cnt <= 0) {
- malloc_failed = 1;
- return 0;
- }
- malloc_failed = 0;
- return malloc(sz);
- }
-
- static void alt_free(void *ptr) {
- free_cnt++;
- free(ptr);
- }
-
- static void complain(int index, example_user_t *users, example_user_t *user)
- {
- int expected_frees = (3 - index);
- if (users) {
- printf("%d: users hash must be empty\n", index);
- }
- if (user->hh.tbl) {
- printf("%d hash table must be empty\n", index);
- }
- if (free_cnt != expected_frees) {
- printf("%d Expected %d frees, only had %d\n", index, expected_frees, free_cnt);
- }
- if (user->mem_failed != 1) {
- printf("%d Expected user->mem_failed(%d) to be 1\n", index, user->mem_failed);
- }
- }
-
- int main()
- {
- example_user_t *users = NULL;
- example_user_t *user = (example_user_t*)malloc(sizeof(example_user_t));
- example_user_t *test;
- example_user_t *users2 = NULL;
- int id = 0;
- int i;
- int saved_cnt;
-
- user->id = id;
-
- #ifdef HASH_BLOOM
- malloc_cnt = 3; // bloom filter must fail
- user->mem_failed = 0;
- user->hh.tbl = (UT_hash_table*)1;
- free_cnt = 0;
- HASH_ADD_INT(users, id, user);
- complain(1, users, user);
- #endif /* HASH_BLOOM */
-
- malloc_cnt = 2; // bucket creation must fail
- user->mem_failed = 0;
- free_cnt = 0;
- user->hh.tbl = (UT_hash_table*)1;
- HASH_ADD_INT(users, id, user);
- complain(2, users, user);
-
- malloc_cnt = 1; // table creation must fail
- user->mem_failed = 0;
- free_cnt = 0;
- user->hh.tbl = (UT_hash_table*)1;
- HASH_ADD_INT(users, id, user);
- complain(3, users, user);
-
- malloc_cnt = 4; // hash must create OK
- user->mem_failed = 0;
- HASH_ADD_INT(users, id, user);
- if (user->mem_failed) {
- printf("mem_failed must be 0, not %d\n", user->mem_failed);
- }
- HASH_FIND_INT(users,&id,test);
- if (!test) {
- printf("test user ID %d not found\n", id);
- }
-
- if (HASH_COUNT(users) != 1) {
- printf("Got HASH_COUNT(users)=%d, should be 1\n", HASH_COUNT(users));
- }
-
- // let's add users until expansion fails.
- malloc_failed = 0;
- free_cnt = 0;
- malloc_cnt = 1;
- for (id = 1; 1; ++id) {
- user = (example_user_t*)malloc(sizeof(example_user_t));
- user->id = id;
- if (id >= 1000) {
- // prevent infinite, or too long of a loop here
- puts("too many allocs before memory request");
- break;
- }
- user->hh.tbl = (UT_hash_table*)1;
- HASH_ADD_INT(users, id, user);
- if (malloc_failed) {
- if (id < 10) {
- puts("there is no way your bucket size is <= 10");
- }
-
- if (user->hh.tbl) {
- puts("user->hh.tbl should be NULL after failure");
- } else if (user->mem_failed != 1) {
- printf("mem_failed should be 1 after failure, not %d\n", user->mem_failed);
- }
-
- if (free_cnt != 0) {
- printf("Expected 0 frees, had %d\n", free_cnt);
- }
-
- // let's make sure all previous IDs are there.
- for (i=0; i<id; ++i) {
- HASH_FIND_INT(users,&i,test);
- if (test == NULL) {
- printf("test user ID %d not found\n", i);
- }
- }
-
- // let's try to add again, but with mem_failed set to 0
- user->hh.tbl = NULL;
- user->mem_failed = 0;
- malloc_failed = 0;
- HASH_ADD_INT(users, id, user);
- if (!malloc_failed) {
- puts("malloc should have been attempted");
- }
- if (user->hh.tbl) {
- puts("user->hh.tbl should be NULL after second failure");
- } else if (user->mem_failed != 1) {
- printf("mem_failed should be 1 after second failure, not %d\n", user->mem_failed);
- }
-
- break;
- }
- }
-
- // let's test HASH_SELECT.
- // let's double the size of the table we've already built.
- saved_cnt = id;
-
- if (HASH_COUNT(users) != (unsigned)saved_cnt) {
- printf("Got HASH_COUNT(users)=%d, should be %d\n", HASH_COUNT(users), saved_cnt);
- }
-
- for (i=0; i < saved_cnt; i++) {
- user = (example_user_t*)malloc(sizeof(example_user_t));
- user->id = ++id;
- malloc_cnt = 20; // don't fail
- HASH_ADD_INT(users, id, user);
- }
-
- HASH_ITER(hh, users, user, test) {
- user->mem_failed = 0;
- }
-
- // HASH_SELECT calls uthash_nonfatal_oom() with an argument of type (void*).
- #undef uthash_nonfatal_oom
- #define uthash_nonfatal_oom(e) do{((example_user_t*)e)->mem_failed=1;}while(0)
-
- malloc_cnt = 0;
- free_cnt = 0;
- HASH_SELECT(hh2, users2, hh, users, all_select);
- if (users2) {
- puts("Nothing should have been copied into users2");
- }
- HASH_ITER(hh, users, user, test) {
- if (user->hh2.tbl) {
- printf("User ID %d has tbl at %p\n", user->id, (void*)user->hh2.tbl);
- }
- if (user->mem_failed != 1) {
- printf("User ID %d has mem_failed(%d), should be 1\n", user->id, user->mem_failed);
- }
- user->mem_failed = 0;
- }
-
- malloc_cnt = 4;
- HASH_SELECT(hh2, users2, hh, users, all_select);
-
- // note about the above.
- // we tried to stick up to 1,000 entries into users,
- // and the malloc failed after saved_cnt. The bucket threshold must have
- // been triggered. We then doubled the amount of entries in user,
- // and just ran HASH_SELECT, trying to copy them into users2.
- // because the order is different, and because we continue after
- // failures, the bucket threshold may get triggered on arbitrary
- // elements, depending on the hash function.
-
- saved_cnt = 0;
- HASH_ITER(hh, users, user, test) {
- example_user_t * user2;
- HASH_FIND(hh2, users2, &user->id, sizeof(int), user2);
- if (user2) {
- if (!user->hh2.tbl) {
- printf("User ID %d has tbl==NULL\n", user->id);
- }
- if (user->mem_failed != 0) {
- printf("User ID %d has mem_failed(%d), expected 0\n", user->id, user->mem_failed);
- }
- } else {
- saved_cnt++;
- if (user->hh2.tbl) {
- printf("User ID %d has tbl at %p, expected 0\n", user->id, (void*)user->hh2.tbl);
- }
- if (user->mem_failed != 1) {
- printf("User ID %d has mem_failed(%d), expected is 1\n", user->id, user->mem_failed);
- }
- }
- }
-
- if (saved_cnt + HASH_CNT(hh2, users2) != HASH_COUNT(users)) {
- printf("Selected elements : %d + %d != %d\n",
- saved_cnt, HASH_CNT(hh2, users2), HASH_COUNT(users));
- }
-
- puts("End");
-
- return 0;
- }
|