diff --git a/include/ucore/htable.h b/include/ucore/htable.h index 3ae18f6..e5dc9d1 100644 --- a/include/ucore/htable.h +++ b/include/ucore/htable.h @@ -168,7 +168,22 @@ void uc_htable_swap(struct UHTable *a, struct UHTable *b); (node_iter) != NULL;\ (node_iter) = uc_htable_next_hash((node_iter))) - /** +/** + * "Safe" variant of UC_HTABLE_FOREACH_HASH, the iterated node + * (or any previously iterated nodes) can be freed while iterating + * + * @param table the struct UHTable* + * @param node_iter struct UHNode* for the current node being iterated + * @param next_iter struct UHNode* for the next node, required by the + * iteration, next_iter must not be altered while iterating. + * @param hash size_t hash for the node(s) to iterate over + */ +#define UC_HTABLE_FOREACH_HASH_SAFE(table, node_iter, next_iter, hash)\ + for ((node_iter) = uc_htable_first_hash((table), (hash));\ + (node_iter) != NULL && (((next_iter) = (node_iter->next)), 1);\ + (node_iter) = (next)) + +/** * Iterate over all nodes in a table * The table must not be changed while iterating. * @@ -180,6 +195,21 @@ void uc_htable_swap(struct UHTable *a, struct UHTable *b); (node_iter) != NULL;\ (node_iter) = uc_htable_next((table), (node_iter))) +/** + * "Safe" variant of UC_HTABLE_FOREACH, the iterated node + * (or any previously iterated nodes) can be freed while iterating + * + * @param table the struct UHTable* + * @param node_iter struct UHNode* for the current node being iterated + * @param next_iter struct UHNode* for the next node, required by the + * iteration, next_iter must not be altered while iterating. +*/ +#define UC_HTABLE_FOREACH_SAFE(table, node_iter, next_iter)\ + for ((node_iter) = uc_htable_first((table));\ + (node_iter) != NULL &&\ + ((next_iter) = uc_htable_next((table), (node_iter)), 1);\ + (node_iter) = (next)) + /** * Check if the given node pointer exists in the hash table diff --git a/test/test_htable.c b/test/test_htable.c index dc3e70b..29e8bca 100644 --- a/test/test_htable.c +++ b/test/test_htable.c @@ -218,6 +218,43 @@ START_TEST (test_htable_remove) END_TEST +START_TEST (test_htable_remove_foreach) + struct MyString str1 = {"One", {0,NULL}}; + struct MyString str2 = {"One", {0,NULL}}; + struct MyString str3 = {"Other", {0,NULL}}; + + struct UHTable table; + struct UHNode *it; + struct UHNode *next; + int rc; + int removed = 0; + + rc = uc_htable_init(&table, 1); + ck_assert_int_eq(rc, 0); + + uc_htable_insert(&table, &str1.node, hash_str(str1.str)); + uc_htable_insert(&table, &str2.node, hash_str(str2.str)); + uc_htable_insert(&table, &str3.node, hash_str(str3.str)); + ck_assert_int_eq(uc_htable_count(&table), 3); + + fail_if(my_find(&table, "One", hash_str("One")) == NULL); + + UC_HTABLE_FOREACH_HASH_SAFE(&table, it, next, hash_str("One")) { + uc_htable_remove(&table, it); + removed++; + + } + + ck_assert_int_eq(removed, 2); + + fail_if(my_find(&table, "One", hash_str("One")) != NULL); + fail_if(my_find(&table, "Other", hash_str("Other")) != &str3); + ck_assert_int_eq(uc_htable_count(&table), 1); + + uc_htable_destroy(&table); + +END_TEST + START_TEST (test_htable_clear) struct MyString str1 = {"One", {0,NULL}}; @@ -282,6 +319,40 @@ START_TEST (test_htable_foreach) END_TEST +START_TEST (test_htable_foreach_safe) + struct MyString str1 = {"One", {0,NULL}}; + struct MyString str2 = {"One", {0,NULL}}; + struct MyString str3 = {"Other",{0,NULL}}; + + struct UHTable table; + struct UHNode *it; + struct UHNode *next; + int rc; + + rc = uc_htable_init(&table, 1000); + ck_assert_int_eq(rc, 0); + + uc_htable_insert(&table, &str1.node, hash_str(str1.str)); + uc_htable_insert(&table, &str2.node, hash_str(str2.str)); + uc_htable_insert(&table, &str3.node, hash_str(str3.str)); + + fail_if(my_find(&table, "Other", hash_str("Other")) != &str3); + + ck_assert_int_eq(uc_htable_count(&table), 3); + + UC_HTABLE_FOREACH_SAFE(&table, it, next) { + uc_htable_remove(&table, it); + } + + fail_if(my_find(&table, "One", hash_str("One")) != NULL); + fail_if(my_find(&table, "Other", hash_str("Other")) != NULL); + + ck_assert_int_eq(uc_htable_count(&table), 0); + + uc_htable_destroy(&table); + +END_TEST + Suite *htable_suite(void) { Suite *s = suite_create("htable"); @@ -293,8 +364,10 @@ Suite *htable_suite(void) tcase_add_test(tc, test_htable_fail_init_resize); tcase_add_test(tc, test_htable_has_node); tcase_add_test(tc, test_htable_remove); + tcase_add_test(tc, test_htable_remove_foreach); tcase_add_test(tc, test_htable_clear); tcase_add_test(tc, test_htable_foreach); + tcase_add_test(tc, test_htable_foreach_safe); suite_add_tcase(s, tc);