// SPDX-FileCopyrightText: pancake // SPDX-License-Identifier: MIT #include "minunit.h" #include #include #include #include #include #include #include #include #include #include #include typedef struct _test_struct { char *name; int age; } Person; bool test_ht_insert_lookup(void) { HtSS *ht = sdb_ht_new(); sdb_ht_insert(ht, "AAAA", "vAAAA"); sdb_ht_insert(ht, "BBBB", "vBBBB"); sdb_ht_insert(ht, "CCCC", "vCCCC"); mu_assert_streq(sdb_ht_find(ht, "BBBB", NULL), "vBBBB", "BBBB value wrong"); mu_assert_streq(sdb_ht_find(ht, "AAAA", NULL), "vAAAA", "AAAA value wrong"); mu_assert_streq(sdb_ht_find(ht, "CCCC", NULL), "vCCCC", "CCCC value wrong"); sdb_ht_free(ht); #ifdef HT_ENABLE_CUSTOM_ELEM_SIZE typedef struct { HtUUKv base; ut64 extra; } CustomKv; HtUUOptions opt = { 0 }; opt.elem_size = sizeof(CustomKv); HtUU *ht_u = ht_uu_new_opt(&opt); for (size_t i = 0; i < 100; ++i) { CustomKv *tmp = NULL; CustomKv kv = { 0 }; kv.base.key = 4 * i; kv.base.value = i + 200; kv.extra = i + 300; ht_uu_insert_kv_ex(ht_u, (HtUUKv *)&kv, false, (HtUUKv **)&tmp); mu_assert_notnull(tmp, "KV is set after rehashing"); mu_assert_eq(tmp->base.value, i + 200, "KV is valid after rehashing"); mu_assert_eq(tmp->extra, i + 300, "KV extra value is valid after rehashing"); } ht_uu_free(ht_u); #endif mu_end; } bool test_ht_update_lookup(void) { HtSS *ht = sdb_ht_new(); sdb_ht_insert(ht, "AAAA", "vAAAA"); sdb_ht_insert(ht, "BBBB", "vBBBB"); // test update to add a new element sdb_ht_update(ht, "CCCC", "vCCCC"); mu_assert_streq(sdb_ht_find(ht, "CCCC", NULL), "vCCCC", "CCCC value wrong"); // test update to replace an existing element sdb_ht_update(ht, "AAAA", "vDDDD"); mu_assert_streq(sdb_ht_find(ht, "AAAA", NULL), "vDDDD", "DDDD value wrong"); sdb_ht_free(ht); mu_end; } bool test_ht_delete(void) { HtSS *ht = sdb_ht_new(); mu_assert("nothing should be deleted", !sdb_ht_delete(ht, "non existing")); sdb_ht_insert(ht, "AAAA", "vAAAA"); mu_assert("AAAA should be deleted", sdb_ht_delete(ht, "AAAA")); mu_assert("AAAA still there", !sdb_ht_find(ht, "AAAA", NULL)); sdb_ht_free(ht); mu_end; } bool test_ht_insert_kvp(void) { HtSS *ht = sdb_ht_new(); SdbKv *kv = sdbkv_new("AAAA", "vAAAA"); mu_assert("AAAA shouldn't exist", !sdb_ht_find_kvp(ht, "AAAA", NULL)); sdb_ht_insert_kvp(ht, kv, false); free(kv); mu_assert("AAAA should exist", sdb_ht_find_kvp(ht, "AAAA", NULL)); SdbKv *kv2 = sdbkv_new("AAAA", "vNEWAAAA"); mu_assert("AAAA shouldn't be replaced", !sdb_ht_insert_kvp(ht, kv2, false)); mu_assert("AAAA should be replaced", sdb_ht_insert_kvp(ht, kv2, true)); free(kv2); SdbKv *foundkv = sdb_ht_find_kvp(ht, "AAAA", NULL); mu_assert_streq(foundkv->base.value, "vNEWAAAA", "vNEWAAAA should be there"); sdb_ht_free(ht); mu_end; } ut32 create_collision(RZ_UNUSED const char *key) { return 10; } bool test_ht_insert_collision(void) { HtSS *ht = sdb_ht_new(); ht->opt.hashfn = create_collision; ht_ss_insert(ht, "AAAA", "vAAAA"); mu_assert_streq(sdb_ht_find(ht, "AAAA", NULL), "vAAAA", "AAAA should be there"); ht_ss_insert(ht, "BBBB", "vBBBB"); mu_assert_streq(sdb_ht_find(ht, "AAAA", NULL), "vAAAA", "AAAA should still be there"); mu_assert_streq(sdb_ht_find(ht, "BBBB", NULL), "vBBBB", "BBBB should be there"); ht_ss_insert(ht, "CCCC", "vBBBB"); mu_assert_streq(sdb_ht_find(ht, "CCCC", NULL), "vBBBB", "CCCC should be there"); sdb_ht_free(ht); mu_end; } ut32 key2hash(const char *key) { return atoi(key); } bool test_ht_grow(void) { HtSS *ht = sdb_ht_new(); char str[15], vstr[15]; char buf[100]; int i; ht->opt.hashfn = key2hash; for (i = 0; i < 20000; ++i) { snprintf(str, 15, "%d", i); snprintf(vstr, 15, "v%d", i); sdb_ht_insert(ht, str, vstr); } for (i = 0; i < 20000; ++i) { snprintf(str, 15, "%d", i); snprintf(vstr, 15, "v%d", i); char *v = sdb_ht_find(ht, str, NULL); snprintf(buf, 100, "%s/%s should be there", str, vstr); mu_assert(buf, v); snprintf(buf, 100, "%s/%s should be right", str, vstr); mu_assert_streq(v, vstr, buf); } sdb_ht_free(ht); mu_end; } bool test_ht_kvp(void) { HtSS *ht = sdb_ht_new(); SdbKv *kvp = sdbkv_new("AAAA", "vAAAA"); mu_assert_eq(kvp->base.key_len, 4, "key_len should be 4"); mu_assert_eq(kvp->base.value_len, 5, "value_len should be 5"); mu_assert("kvp should be inserted", sdb_ht_insert_kvp(ht, kvp, false)); free(kvp); kvp = sdb_ht_find_kvp(ht, "AAAA", NULL); mu_assert_eq(kvp->base.key_len, 4, "key_len should be 4 after kvp_insert"); mu_assert_eq(kvp->base.value_len, 5, "value_len should be 5 after kvp_insert"); sdb_ht_insert(ht, "BBBB", "vBBBB"); kvp = sdb_ht_find_kvp(ht, "BBBB", NULL); mu_assert_eq(kvp->base.key_len, 4, "key_len should be 4 after insert"); mu_assert_eq(kvp->base.value_len, 5, "value_len should be 5 after insert"); sdb_ht_free(ht); mu_end; } Person *duplicate_person(Person *p) { Person *c = malloc(sizeof(Person)); c->name = strdup(p->name); c->age = p->age; return c; } void free_person(Person *p) { if (!p) { return; } free(p->name); free(p); } size_t calcSizePerson(void *c) { Person *p = c; return sizeof(*p); } bool test_ht_general(void) { int retval = MU_PASSED; bool found = false; Person *p, *person1 = malloc(sizeof(Person)); if (!person1) { mu_cleanup_fail(err_malloc, "person1 malloc"); } person1->name = strdup("radare"); person1->age = 10; Person *person2 = malloc(sizeof(Person)); if (!person2) { mu_cleanup_fail(err_free_person1, "person2 malloc"); } person2->name = strdup("pancake"); person2->age = 9000; HtSP *ht = ht_sp_new(HT_STR_DUP, (HtSPDupValue)duplicate_person, (HtSPFreeValue)free_person); if (!ht) { mu_cleanup_fail(err_free_persons, "ht alloc"); } ht_sp_insert(ht, "radare", (void *)person1); ht_sp_insert(ht, "pancake", (void *)person2); p = ht_sp_find(ht, "radare", &found); mu_assert("radare not found", found); mu_assert_streq(p->name, "radare", "wrong person"); mu_assert_eq(p->age, 10, "wrong radare age"); p = ht_sp_find(ht, "pancake", &found); mu_assert("radare not found", found); mu_assert_streq(p->name, "pancake", "wrong person"); mu_assert_eq(p->age, 9000, "wrong pancake age"); (void)ht_sp_find(ht, "not", &found); mu_assert("found but it should not exists", !found); ht_sp_delete(ht, "pancake"); p = ht_sp_find(ht, "pancake", &found); mu_assert("pancake was deleted", !found); ht_sp_insert(ht, "pancake", (void *)person2); ht_sp_delete(ht, "radare"); ht_sp_update(ht, "pancake", (void *)person1); p = ht_sp_find(ht, "pancake", &found); mu_assert("pancake was updated", found); mu_assert_streq(p->name, "radare", "wrong person"); mu_assert_eq(p->age, 10, "wrong age"); ht_sp_free(ht); err_free_persons: free_person(person2); err_free_person1: free_person(person1); err_malloc: mu_cleanup_end; } bool should_not_be_caled(void *user, const char *k, const void *v) { mu_fail("this function should not be called"); return false; } bool test_empty_ht(void) { HtSP *ht = ht_sp_new(HT_STR_DUP, NULL, NULL); ht_sp_foreach(ht, should_not_be_caled, NULL); void *r = ht_sp_find(ht, "key1", NULL); mu_assert_null(r, "key1 should not be present"); ht_sp_free(ht); mu_end; } bool test_insert(void) { HtSS *ht = ht_ss_new(HT_STR_CONST, HT_STR_CONST); void *r; bool res; bool found; res = ht_ss_insert(ht, "key1", "value1"); mu_assert("key1 should be a new element", res); r = ht_ss_find(ht, "key1", &found); mu_assert("found should be true", found); mu_assert_streq(r, "value1", "value1 should be retrieved"); res = ht_ss_insert(ht, "key1", "value2"); mu_assert("key1 should be an already existing element", !res); r = ht_ss_find(ht, "key1", &found); mu_assert_streq(r, "value1", "value1 should be retrieved"); mu_assert("found should be true", found); r = ht_ss_find(ht, "key2", &found); mu_assert_null(r, "key2 should not be present"); mu_assert("found for key2 should be false", !found); ht_ss_free(ht); mu_end; } bool test_update(void) { HtSS *ht = ht_ss_new(HT_STR_DUP, HT_STR_DUP); bool found; ht_ss_insert(ht, "key1", "value1"); ht_ss_update(ht, "key1", "value2"); void *r = ht_ss_find(ht, "key1", &found); mu_assert_streq(r, "value2", "value2 should be retrieved"); mu_assert("found should be true", found); ht_ss_free(ht); mu_end; } bool test_delete(void) { HtSS *ht = ht_ss_new(HT_STR_DUP, HT_STR_DUP); bool found; ht_ss_insert(ht, "key1", "value1"); ht_ss_delete(ht, "key1"); void *r = ht_ss_find(ht, "key1", &found); mu_assert_null(r, "key1 should not be found"); mu_assert("found should be false", !found); ht_ss_free(ht); mu_end; } bool test_rehash_on_delete(void) { HtUU *ht = ht_uu_new(); ht->opt.hashfn = (HtUUHashFunction)create_collision; for (ut32 i = 0; i < 56; i++) { mu_assert_true(ht_uu_insert(ht, i, i * 100), "failed to insert element"); } for (ut32 i = 0; i < 56; i++) { mu_assert_true(ht_uu_delete(ht, i), "failed to delete element"); } for (ut32 i = 0; i < 56; i++) { mu_assert_true(ht_uu_insert(ht, i, i * 100), "failed to insert element"); } for (ut32 i = 0; i < 56; i++) { mu_assert_true(ht_uu_update_key(ht, i, i + 1000), "failed to update element key"); } mu_assert_eq(ht_uu_size(ht), 56, "invalid table size"); ht_uu_free(ht); mu_end; } bool test_ht_delete_optimized(void) { // Test case for the "deletion trick" optimization. If the optimized implementation places // incorrect "empty" slot, this would corrupt the hashtable and make some keys unreachable bool found = false; for (ut32 size = 1; size <= 32; size++) { for (ut32 delete_key = 0; delete_key < size; delete_key++) { HtUU *ht = ht_uu_new(); ht->opt.hashfn = (HtUUHashFunction)create_collision; // Insert all for (ut32 i = 0; i < size; i++) { ht_uu_insert(ht, i, i * 100); } // Delete 1 ht_uu_delete(ht, delete_key); // Confirm for (ut32 i = 0; i < size; i++) { HtUUKv *kv = ht_uu_find_kv(ht, i, &found); if (i == delete_key) { mu_assert_null(kv, "element expected to be deleted"); mu_assert_false(found, "element expected to be deleted"); } else { mu_assert_notnull(kv, "key not found"); mu_assert_true(found, "key not found"); mu_assert_eq(kv->value, i * 100, "incorrect value"); } } mu_assert_eq(ht_uu_size(ht), size - 1, "invalid table size"); ht_uu_free(ht); } } mu_end; } bool test_clear(void) { HtSS *ht = ht_ss_new(HT_STR_DUP, HT_STR_DUP); ht_ss_insert(ht, "key1", "value1"); ht_ss_insert(ht, "key2", "value2"); ht_ss_insert(ht, "key3", "value3"); mu_assert_eq(ht_ss_size(ht), 3, "Wrong size"); ht_ss_clear(ht); mu_assert_eq(ht_ss_size(ht), 0, "Wrong size"); ht_ss_insert(ht, "key1", "value1"); ht_ss_insert(ht, "key2", "value2"); ht_ss_insert(ht, "key3", "value3"); mu_assert_eq(ht_ss_size(ht), 3, "Wrong size"); ht_ss_free(ht); mu_end; } static bool grow_1_found[3]; static bool grow_1_foreach(void *user, const char *k, int v) { grow_1_found[v] = true; return true; } bool test_grow_1(void) { HtSP *ht = ht_sp_new(HT_STR_DUP, NULL, NULL); int i; for (i = 0; i < 3; ++i) { grow_1_found[i] = false; } ht_sp_insert(ht, "key0", (void *)0); ht_sp_insert(ht, "key1", (void *)1); ht_sp_insert(ht, "key2", (void *)2); ht_sp_foreach(ht, (HtSPForeachCallback)grow_1_foreach, NULL); for (i = 0; i < 3; ++i) { if (!grow_1_found[i]) { fprintf(stderr, "i = %d\n", i); mu_fail("An element has not been traversed"); } } ht_sp_free(ht); mu_end; } bool test_grow_2(void) { HtSS *ht = ht_ss_new(HT_STR_DUP, HT_STR_DUP); char *r; bool found; int i; for (i = 0; i < 3000; ++i) { char buf[20], buf2[20]; snprintf(buf, 20, "key%d", i); snprintf(buf2, 20, "value%d", i); ht_ss_insert(ht, buf, buf2); } r = ht_ss_find(ht, "key1", &found); mu_assert_streq(r, "value1", "value1 should be retrieved"); mu_assert("found should be true", found); r = ht_ss_find(ht, "key2000", &found); mu_assert_streq(r, "value2000", "value2000 should be retrieved"); mu_assert("found should be true", found); r = ht_ss_find(ht, "key4000", &found); mu_assert_null(r, "key4000 should not be there"); mu_assert("found should be false", !found); ht_ss_free(ht); mu_end; } bool test_grow_3(void) { HtSS *ht = ht_ss_new(HT_STR_DUP, HT_STR_DUP); char *r; bool found; int i; for (i = 0; i < 3000; ++i) { char buf[20], buf2[20]; snprintf(buf, 20, "key%d", i); snprintf(buf2, 20, "value%d", i); ht_ss_insert(ht, buf, buf2); } for (i = 0; i < 3000; i += 3) { char buf[20]; snprintf(buf, 20, "key%d", i); ht_ss_delete(ht, buf); } r = ht_ss_find(ht, "key1", &found); mu_assert_streq(r, "value1", "value1 should be retrieved"); mu_assert("found should be true", found); r = ht_ss_find(ht, "key2000", &found); mu_assert_streq(r, "value2000", "value2000 should be retrieved"); mu_assert("found should be true", found); r = ht_ss_find(ht, "key4000", &found); mu_assert_null(r, "key4000 should not be there"); mu_assert("found should be false", !found); r = ht_ss_find(ht, "key0", &found); mu_assert_null(r, "key0 should not be there"); mu_assert("found should be false", !found); for (i = 1; i < 3000; i += 3) { char buf[20]; snprintf(buf, 20, "key%d", i); ht_ss_delete(ht, buf); } r = ht_ss_find(ht, "key1", &found); mu_assert_null(r, "key1 should not be there"); mu_assert("found should be false", !found); ht_ss_free(ht); mu_end; } bool test_grow_4(void) { HtSS *ht = ht_ss_new(HT_STR_DUP, HT_STR_OWN); char *r; bool found; int i; for (i = 0; i < 3000; ++i) { char buf[20], *buf2; snprintf(buf, 20, "key%d", i); buf2 = malloc(20); snprintf(buf2, 20, "value%d", i); ht_ss_insert(ht, buf, buf2); } r = ht_ss_find(ht, "key1", &found); mu_assert_streq(r, "value1", "value1 should be retrieved"); mu_assert("found should be true", found); r = ht_ss_find(ht, "key2000", &found); mu_assert_streq(r, "value2000", "value2000 should be retrieved"); mu_assert("found should be true", found); for (i = 0; i < 3000; i += 3) { char buf[20]; snprintf(buf, 20, "key%d", i); ht_ss_delete(ht, buf); } r = ht_ss_find(ht, "key2000", &found); mu_assert_streq(r, "value2000", "value2000 should be retrieved"); mu_assert("found should be true", found); r = ht_ss_find(ht, "key0", &found); mu_assert_null(r, "key0 should not be there"); mu_assert("found should be false", !found); for (i = 1; i < 3000; i += 3) { char buf[20]; snprintf(buf, 20, "key%d", i); ht_ss_delete(ht, buf); } r = ht_ss_find(ht, "key1", &found); mu_assert_null(r, "key1 should not be there"); mu_assert("found should be false", !found); ht_ss_free(ht); mu_end; } bool foreach_delete_cb(void *user, const ut64 key, const void *v) { HtUP *ht = (HtUP *)user; ht_up_delete(ht, key); return true; } bool test_foreach_delete(void) { HtUP *ht = ht_up_new((HtUPDupValue)strdup, free); // create a collision ht_up_insert(ht, 0, "value1"); ht_up_insert(ht, ht->size, "value2"); ht_up_insert(ht, ht->size * 2, "value3"); ht_up_insert(ht, ht->size * 3, "value4"); ht_up_foreach(ht, foreach_delete_cb, ht); ht_up_foreach(ht, (HtUPForeachCallback)should_not_be_caled, NULL); ht_up_free(ht); mu_end; } bool test_update_key(void) { bool res; HtUP *ht = ht_up_new((HtUPDupValue)strdup, free); // create a collision ht_up_insert(ht, 0, "value1"); ht_up_insert(ht, 0xdeadbeef, "value2"); ht_up_insert(ht, 0xcafebabe, "value3"); res = ht_up_update_key(ht, 0xcafebabe, 0x10000); mu_assert("cafebabe should be updated", res); res = ht_up_update_key(ht, 0xdeadbeef, 0x10000); mu_assert("deadbeef should NOT be updated, because there's already an element at 0x10000", !res); const char *v = ht_up_find(ht, 0x10000, NULL); mu_assert_streq(v, "value3", "value3 should be at 0x10000"); v = ht_up_find(ht, 0xdeadbeef, NULL); mu_assert_streq(v, "value2", "value2 should remain at 0xdeadbeef"); ht_up_free(ht); mu_end; } bool test_ht_pu_ops(void) { bool res; ut64 val; HtSU *ht = ht_su_new(HT_STR_DUP); ht_su_insert(ht, "key1", 0xcafebabe); val = ht_su_find(ht, "key1", &res); mu_assert_eq(val, 0xcafebabe, "0xcafebabe should be retrieved"); mu_assert("found should be true", res); res = ht_su_insert(ht, "key1", 0xdeadbeefdeadbeef); mu_assert("key1 should be an already existing element", !res); val = ht_su_find(ht, "key1", &res); mu_assert_eq(val, 0xcafebabe, "0xcafebabe should still be retrieved"); res = ht_su_update(ht, "key1", 0xdeadbeefdeadbeef); mu_assert("key1 should be updated", res); val = ht_su_find(ht, "key1", &res); mu_assert_eq(val, 0xdeadbeefdeadbeef, "0xdeadbeefdeadbeef should be retrieved"); mu_assert("found should be true", res); res = ht_su_delete(ht, "key1"); mu_assert("key1 should be deleted", res); val = ht_su_find(ht, "key1", &res); mu_assert_eq(val, 0, "0 should be retrieved"); mu_assert("found should be false", !res); ht_su_free(ht); mu_end; } bool test_insert_update_ex(void) { HtSU *ht = ht_su_new(HT_STR_CONST); HtSUKv *inserted_kv = NULL; mu_assert_eq(ht_su_insert_ex(ht, "foobar", 1337, &inserted_kv), HT_RC_INSERTED, "HT_RC_INSERTED"); mu_assert_notnull(inserted_kv, "inserted_kv"); mu_assert_streq(inserted_kv->key, "foobar", "key"); mu_assert_eq(inserted_kv->value, 1337, "value"); HtSUKv *existing_kv = NULL; mu_assert_eq(ht_su_insert_ex(ht, "foobar", 101, &existing_kv), HT_RC_EXISTING, "HT_RC_EXISTING"); mu_assert_notnull(existing_kv, "existing_kv"); mu_assert_streq(existing_kv->key, "foobar", "key"); mu_assert_eq(existing_kv->value, 1337, "value"); HtSUKv *inserted_kv2 = NULL; mu_assert_eq(ht_su_update_ex(ht, "deadbeef", 404, &inserted_kv2), HT_RC_INSERTED, "HT_RC_INSERTED"); mu_assert_notnull(inserted_kv2, "inserted_kv2"); mu_assert_streq(inserted_kv2->key, "deadbeef", "key"); mu_assert_eq(inserted_kv2->value, 404, "value"); HtSUKv *updated_kv = NULL; mu_assert_eq(ht_su_update_ex(ht, "deadbeef", 123456, &updated_kv), HT_RC_UPDATED, "HT_RC_UPDATED"); mu_assert_notnull(updated_kv, "updated_kv"); mu_assert_streq(updated_kv->key, "deadbeef", "key"); mu_assert_eq(updated_kv->value, 123456, "value"); HtUU *ht2 = ht_uu_new(); for (size_t i = 0; i < 100; ++i) { HtUUKv *tmp = NULL; ht_uu_insert_ex(ht2, 4 * i, i + 200, &tmp); mu_assert_notnull(tmp, "KV is set after rehashing"); mu_assert_eq(tmp->value, i + 200, "KV is valid after rehashing"); } ht_su_free(ht); ht_uu_free(ht2); mu_end; } bool test_ht_size(void) { HtUU *ht = ht_uu_new(); mu_assert_eq(ht_uu_size(ht), 0, "Length wrong."); ht_uu_insert(ht, 0x5050505, 0x5050505); ht_uu_insert(ht, 0x5050505, 0x5050505); ht_uu_insert(ht, 0x6060606, 0x6060606); ht_uu_insert(ht, 0x7070707, 0x7070707); ht_uu_insert(ht, 0x7070707, 0x7070707); mu_assert_eq(ht_uu_size(ht), 3, "Length wrong."); bool found = false; ht_uu_find(ht, 0x5050505, &found); mu_assert_true(found, "Value was not added."); ht_uu_find(ht, 0x6060606, &found); mu_assert_true(found, "Value was not added."); ht_uu_find(ht, 0x7070707, &found); mu_assert_true(found, "Value was not added."); ht_uu_delete(ht, 0x7070707); ht_uu_find(ht, 0x7070707, &found); mu_assert_false(found, "Value was not deleted."); mu_assert_eq(ht_uu_size(ht), 2, "Length wrong."); // Double delete ht_uu_delete(ht, 0x7070707); ht_uu_find(ht, 0x7070707, &found); mu_assert_false(found, "Value was not deleted."); mu_assert_eq(ht_uu_size(ht), 2, "Length wrong."); ht_uu_free(ht); mu_end; } bool test_ht_uu_iter(void) { HtUU *ht = ht_uu_new(); ut32 icnt = 0; const ut64 *im_elem; RzIterator *it = ht_uu_as_iter(ht); rz_iterator_foreach(it, im_elem) { icnt++; } rz_iterator_free(it); mu_assert_eq(icnt, 0, "Wrong number of iterations"); ht_uu_insert(ht, 0x1010101, 0x1010101); ht_uu_insert(ht, 0x2020202, 0x2020202); ht_uu_insert(ht, 0x3030303, 0x3030303); ht_uu_insert(ht, 0x4040404, 0x4040404); ht_uu_insert(ht, 0x5050505, 0x5050505); icnt = 0; it = ht_uu_as_iter(ht); rz_iterator_foreach(it, im_elem) { icnt++; mu_assert_true( *im_elem == 0x1010101 || *im_elem == 0x2020202 || *im_elem == 0x3030303 || *im_elem == 0x4040404 || *im_elem == 0x5050505, "Value mismtach"); } rz_iterator_free(it); mu_assert_eq(icnt, 5, "Wrong number of iterations"); icnt = 0; // Test write of value ut64 *m_elem; it = ht_uu_as_iter_mut(ht); rz_iterator_foreach(it, m_elem) { icnt++; if (*m_elem == 0x1010101) { *m_elem = 0x0; } } rz_iterator_free(it); mu_assert_eq(icnt, 5, "Wrong number of iterations"); bool found = false; ut64 v = ht_uu_find(ht, 0x1010101, &found); mu_assert_true(found, "Key not in hash map"); mu_assert_eq(v, 0x0, "Value didn't change."); ht_uu_free(ht); mu_end; } bool test_ht_ss_iter(void) { HtSS *ht = ht_ss_new(HT_STR_CONST, HT_STR_CONST); ut32 icnt = 0; const char **im_elem; RzIterator *it = ht_ss_as_iter(ht); rz_iterator_foreach(it, im_elem) { icnt++; } rz_iterator_free(it); mu_assert_eq(icnt, 0, "Wrong number of iterations"); ht_ss_insert(ht, "0x1010101", "0x1010101"); ht_ss_insert(ht, "0x2020202", "0x2020202"); ht_ss_insert(ht, "0x3030303", "0x3030303"); ht_ss_insert(ht, "0x4040404", "0x4040404"); ht_ss_insert(ht, "0x5050505", "0x5050505"); icnt = 0; it = ht_ss_as_iter(ht); rz_iterator_foreach(it, im_elem) { icnt++; mu_assert_true( RZ_STR_EQ(*im_elem, "0x1010101") || RZ_STR_EQ(*im_elem, "0x2020202") || RZ_STR_EQ(*im_elem, "0x3030303") || RZ_STR_EQ(*im_elem, "0x4040404") || RZ_STR_EQ(*im_elem, "0x5050505"), "Value mismtach"); } rz_iterator_free(it); mu_assert_eq(icnt, 5, "Wrong number of iterations"); icnt = 0; // Test write of value char **m_elem; it = ht_ss_as_iter_mut(ht); rz_iterator_foreach(it, m_elem) { icnt++; if (RZ_STR_EQ(*m_elem, "0x1010101")) { *m_elem = "0x0"; } } rz_iterator_free(it); mu_assert_eq(icnt, 5, "Wrong number of iterations"); bool found = false; const char *v = ht_ss_find(ht, "0x1010101", &found); mu_assert_true(found, "Key not in hash map"); mu_assert_streq(v, "0x0", "Value didn't change."); ht_ss_free(ht); mu_end; } bool test_set_u(void) { RzSetU *set_u = rz_set_u_new(); rz_set_u_add(set_u, 0x5050505); rz_set_u_add(set_u, 0x5050505); rz_set_u_add(set_u, 0x6060606); rz_set_u_add(set_u, 0x7070707); rz_set_u_add(set_u, 0x7070707); mu_assert_eq(rz_set_u_size(set_u), 3, "Length wrong."); mu_assert_true(rz_set_u_contains(set_u, 0x5050505), "Value was not added."); mu_assert_true(rz_set_u_contains(set_u, 0x6060606), "Value was not added."); mu_assert_true(rz_set_u_contains(set_u, 0x7070707), "Value was not added."); rz_set_u_delete(set_u, 0x7070707); mu_assert_false(rz_set_u_contains(set_u, 0x7070707), "Value was not deleted."); mu_assert_eq(rz_set_u_size(set_u), 2, "Length wrong."); // Double delete rz_set_u_delete(set_u, 0x7070707); mu_assert_eq(rz_set_u_size(set_u), 2, "Length wrong."); size_t x = 0; const ut64 *im_elem; RzIterator *it = rz_set_u_as_iter(set_u); rz_iterator_foreach(it, im_elem) { x++; bool matches = *im_elem == 0x5050505 || *im_elem == 0x6060606; mu_assert_true(matches, "Set contained ill-formed value."); } rz_iterator_free(it); mu_assert_eq(x, 2, "Foreach hasn't iterated the correct number of times."); rz_set_u_delete(set_u, 0x6060606); mu_assert_eq(rz_set_u_size(set_u), 1, "Length wrong."); rz_set_u_delete(set_u, 0x5050505); mu_assert_eq(rz_set_u_size(set_u), 0, "Length wrong."); it = rz_set_u_as_iter(set_u); rz_iterator_foreach(it, im_elem) { mu_assert("Should not be reached.", false); } rz_iterator_free(it); rz_set_u_add(set_u, 0x53e0); rz_set_u_add(set_u, 0x53bc); mu_assert_eq(rz_set_u_size(set_u), 2, "Wrong size"); rz_set_u_clear(set_u); mu_assert_eq(rz_set_u_size(set_u), 0, "Wrong size"); rz_set_u_add(set_u, 0x53e0); rz_set_u_add(set_u, 0x53bc); x = 0; it = rz_set_u_as_iter(set_u); rz_iterator_foreach(it, im_elem) { x++; } rz_iterator_free(it); mu_assert_eq(x, 2, "Foreach hasn't iterated the correct number of times."); rz_set_u_delete(set_u, 0x53e0); rz_set_u_delete(set_u, 0x53bc); rz_set_u_add(set_u, 0); rz_set_u_add(set_u, 1); rz_set_u_add(set_u, 2); rz_set_u_add(set_u, 3); // Add an address as key which is far away from the heap addresses. rz_set_u_add(set_u, 100000000); mu_assert_true(rz_set_u_contains(set_u, 100000000), "Not contained."); mu_assert_eq(rz_set_u_size(set_u), 5, "count"); mu_assert_false(rz_set_u_contains(set_u, 6), "should not be here."); x = 0; it = rz_set_u_as_iter(set_u); rz_iterator_foreach(it, im_elem) { x++; } rz_iterator_free(it); mu_assert_eq(x, 5, "Foreach hasn't iterated the correct number of times."); rz_set_u_clear(set_u); mu_assert_eq(rz_set_u_take(set_u), UT64_MAX, "Invalid take should return UT64_MAX"); rz_set_u_add(set_u, 100000000); mu_assert_eq(rz_set_u_size(set_u), 1, "Add failed"); mu_assert_eq(rz_set_u_take(set_u), 100000000, "Take failed"); mu_assert_eq(rz_set_u_size(set_u), 0, "Take did not remove element"); rz_set_u_free(set_u); mu_end; } bool test_set_s(void) { RzSetS *set_s = rz_set_s_new(HT_STR_CONST); rz_set_s_add(set_s, "0x5050505"); rz_set_s_add(set_s, "0x5050505"); rz_set_s_add(set_s, "0x6060606"); rz_set_s_add(set_s, "0x7070707"); rz_set_s_add(set_s, "0x7070707"); mu_assert_eq(rz_set_s_size(set_s), 3, "Length wrong."); mu_assert_true(rz_set_s_contains(set_s, "0x5050505"), "Value was not added."); mu_assert_true(rz_set_s_contains(set_s, "0x6060606"), "Value was not added."); mu_assert_true(rz_set_s_contains(set_s, "0x7070707"), "Value was not added."); rz_set_s_delete(set_s, "0x7070707"); mu_assert_false(rz_set_s_contains(set_s, "0x7070707"), "Value was not deleted."); mu_assert_eq(rz_set_s_size(set_s), 2, "Length wrong."); // Double delete rz_set_s_delete(set_s, "0x7070707"); mu_assert_eq(rz_set_s_size(set_s), 2, "Length wrong."); size_t x = 0; const char **im_elem; RzIterator *it = rz_set_s_as_iter(set_s); rz_iterator_foreach(it, im_elem) { x++; bool matches = RZ_STR_EQ(*im_elem, "0x5050505") || RZ_STR_EQ(*im_elem, "0x6060606"); mu_assert_true(matches, "Set contained ill-formed value."); } rz_iterator_free(it); mu_assert_eq(x, 2, "Foreach hasn't iterated the correct number of times."); rz_set_s_delete(set_s, "0x6060606"); mu_assert_eq(rz_set_s_size(set_s), 1, "Length wrong."); rz_set_s_delete(set_s, "0x5050505"); mu_assert_eq(rz_set_s_size(set_s), 0, "Length wrong."); it = rz_set_s_as_iter(set_s); rz_iterator_foreach(it, im_elem) { mu_assert("Should not be reached.", false); } rz_iterator_free(it); rz_set_s_add(set_s, "0x53e0"); rz_set_s_add(set_s, "0x53bc"); x = 0; it = rz_set_s_as_iter(set_s); rz_iterator_foreach(it, im_elem) { x++; } rz_iterator_free(it); mu_assert_eq(x, 2, "Foreach hasn't iterated the correct number of times."); rz_set_s_delete(set_s, "0x53e0"); rz_set_s_delete(set_s, "0x53bc"); rz_set_s_free(set_s); mu_end; } int all_tests() { mu_run_test(test_ht_insert_lookup); mu_run_test(test_ht_update_lookup); mu_run_test(test_ht_delete); mu_run_test(test_ht_delete_optimized); mu_run_test(test_rehash_on_delete); mu_run_test(test_ht_insert_kvp); mu_run_test(test_ht_insert_collision); mu_run_test(test_ht_grow); mu_run_test(test_ht_kvp); mu_run_test(test_ht_general); mu_run_test(test_empty_ht); mu_run_test(test_insert); mu_run_test(test_update); mu_run_test(test_delete); mu_run_test(test_clear); mu_run_test(test_grow_1); mu_run_test(test_grow_2); mu_run_test(test_grow_3); mu_run_test(test_grow_4); mu_run_test(test_foreach_delete); mu_run_test(test_update_key); mu_run_test(test_ht_pu_ops); mu_run_test(test_insert_update_ex); mu_run_test(test_ht_size); mu_run_test(test_ht_uu_iter); mu_run_test(test_ht_ss_iter); mu_run_test(test_set_u); mu_run_test(test_set_s); return tests_passed != tests_run; } int main(int argc, char **argv) { return all_tests(); }