Refactor and simplify isc_symtab

This commit does several changes to isc_symtab:

1. Rewrite the isc_symtab to internally use isc_hashmap instead of
   hand-stiched hashtable.

2. Create a new isc_symtab_define_and_return() api, which returns
   the already defined symvalue on ISC_R_EXISTS; this allows users
   of the API to skip the isc_symtab_lookup()+isc_symtab_define()
   calls and directly call isc_symtab_define_and_return().

3. Merge isccc_symtab into isc_symtab - the only missing function
   was isccc_symtab_foreach() that was merged into isc_symtab API.

4. Add full set of unit tests for the isc_symtab API.
This commit is contained in:
alessio
2024-11-29 10:02:13 +01:00
parent ebf1606f38
commit 53991ecc14
12 changed files with 630 additions and 557 deletions

View File

@@ -28,105 +28,254 @@
#include <tests/isc.h>
static void
undefine(char *key, unsigned int type, isc_symvalue_t value, void *arg) {
UNUSED(arg);
#define TEST_NITEMS 10000
assert_int_equal(type, 1);
static void
undefine(char *key, unsigned int type ISC_ATTR_UNUSED, isc_symvalue_t value,
void *arg ISC_ATTR_UNUSED) {
isc_mem_free(mctx, key);
isc_mem_free(mctx, value.as_pointer);
}
/* test symbol table growth */
ISC_RUN_TEST_IMPL(symtab_grow) {
ISC_RUN_TEST_IMPL(symtab_define) {
isc_result_t result;
isc_symtab_t *st = NULL;
isc_symtab_t *symtab = NULL;
isc_symvalue_t value;
isc_symvalue_t found;
isc_symexists_t policy = isc_symexists_reject;
char str[16], *key;
snprintf(str, sizeof(str), "%p", "define");
key = isc_mem_strdup(mctx, str);
isc_symtab_create(mctx, undefine, NULL, false, &symtab);
assert_non_null(symtab);
value.as_pointer = isc_mem_strdup(mctx, key);
assert_non_null(value.as_pointer);
result = isc_symtab_define(symtab, key, 1, value, policy);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_symtab_lookup(symtab, key, 1, &found);
assert_int_equal(result, ISC_R_SUCCESS);
assert_string_equal(value.as_pointer, found.as_pointer);
result = isc_symtab_lookup(symtab, key, 2, NULL);
assert_int_equal(result, ISC_R_NOTFOUND);
isc_symtab_destroy(&symtab);
}
ISC_RUN_TEST_IMPL(symtab_undefine) {
isc_result_t result;
isc_symtab_t *symtab = NULL;
isc_symvalue_t value;
isc_symexists_t policy = isc_symexists_reject;
int i;
UNUSED(state);
/* We need a separate copy of the key to prevent an use-after-free */
char str[16], *key, *key_after_undefine;
snprintf(str, sizeof(str), "%p", "undefine");
isc_symtab_create(mctx, 3, undefine, NULL, false, &st);
assert_non_null(st);
key = isc_mem_strdup(mctx, str);
key_after_undefine = isc_mem_strdup(mctx, str);
isc_symtab_create(mctx, undefine, NULL, false, &symtab);
assert_non_null(symtab);
value.as_pointer = isc_mem_strdup(mctx, key);
assert_non_null(value.as_pointer);
result = isc_symtab_define(symtab, key, 1, value, policy);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_symtab_lookup(symtab, key_after_undefine, 1, NULL);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_symtab_undefine(symtab, key, 1);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_symtab_lookup(symtab, key_after_undefine, 1, NULL);
assert_int_equal(result, ISC_R_NOTFOUND);
isc_symtab_destroy(&symtab);
/* key will be freed by isc_symtab_undefine, so we don't need to free
* it again
*/
isc_mem_free(mctx, key_after_undefine);
}
ISC_RUN_TEST_IMPL(symtab_replace) {
isc_result_t result;
isc_symtab_t *symtab = NULL;
isc_symvalue_t value1;
isc_symvalue_t value2;
isc_symvalue_t found;
isc_symexists_t policy = isc_symexists_replace;
char str[16], *key1, *key2;
snprintf(str, sizeof(str), "%p", "replace");
key1 = isc_mem_strdup(mctx, str);
key2 = isc_mem_strdup(mctx, str);
isc_symtab_create(mctx, undefine, NULL, false, &symtab);
assert_non_null(symtab);
value1.as_pointer = isc_mem_strdup(mctx, key1);
assert_non_null(value1.as_pointer);
value2.as_pointer = isc_mem_strdup(mctx, key2);
assert_non_null(value2.as_pointer);
result = isc_symtab_define(symtab, key1, 1, value1, policy);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_symtab_lookup(symtab, key1, 1, &found);
assert_int_equal(result, ISC_R_SUCCESS);
assert_string_equal(value1.as_pointer, found.as_pointer);
result = isc_symtab_define(symtab, key2, 1, value2, policy);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_symtab_lookup(symtab, key2, 1, &found);
assert_int_equal(result, ISC_R_SUCCESS);
assert_string_equal(value2.as_pointer, found.as_pointer);
result = isc_symtab_undefine(symtab, key2, 1);
assert_int_equal(result, ISC_R_SUCCESS);
isc_symtab_destroy(&symtab);
}
ISC_RUN_TEST_IMPL(symtab_reject) {
isc_result_t result;
isc_symtab_t *symtab = NULL;
isc_symvalue_t value1;
isc_symvalue_t value2;
isc_symvalue_t found;
isc_symexists_t policy = isc_symexists_reject;
char str[16], *key1, *key2;
snprintf(str, sizeof(str), "%p", "reject");
key1 = isc_mem_strdup(mctx, str);
key2 = isc_mem_strdup(mctx, str);
isc_symtab_create(mctx, undefine, NULL, false, &symtab);
assert_non_null(symtab);
value1.as_pointer = isc_mem_strdup(mctx, key1);
assert_non_null(value1.as_pointer);
value2.as_pointer = isc_mem_strdup(mctx, key2);
assert_non_null(value2.as_pointer);
result = isc_symtab_define(symtab, key1, 1, value1, policy);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_symtab_lookup(symtab, key1, 1, &found);
assert_int_equal(result, ISC_R_SUCCESS);
assert_string_equal(value1.as_pointer, found.as_pointer);
result = isc_symtab_define_and_return(symtab, key2, 1, value2, policy,
&found);
assert_int_equal(result, ISC_R_EXISTS);
assert_string_equal(value1.as_pointer, found.as_pointer);
result = isc_symtab_lookup(symtab, key2, 1, &found);
assert_int_equal(result, ISC_R_SUCCESS);
assert_string_equal(value1.as_pointer, found.as_pointer);
result = isc_symtab_undefine(symtab, key1, 1);
assert_int_equal(result, ISC_R_SUCCESS);
undefine(key2, 1, value2, NULL);
isc_symtab_destroy(&symtab);
}
static bool
peek(char *key ISC_ATTR_UNUSED, unsigned int type,
isc_symvalue_t value ISC_ATTR_UNUSED, void *arg) {
bool *seen = arg;
size_t i = type - 1;
assert_false(seen[i]);
seen[i] = true;
return i % 2;
}
ISC_RUN_TEST_IMPL(symtab_foreach) {
isc_result_t result;
isc_symtab_t *symtab = NULL;
isc_symvalue_t value;
isc_symexists_t policy = isc_symexists_reject;
bool seen[TEST_NITEMS] = { 0 };
isc_symtab_create(mctx, undefine, NULL, false, &symtab);
/* Nothing should be in the table yet */
assert_non_null(symtab);
/*
* Put 1024 entries in the table (this should necessate
* regrowing the hash table several times
* Put TEST_NITEMS entries in the table.
*/
for (i = 0; i < 1024; i++) {
char str[16], *key;
for (size_t i = 0; i < TEST_NITEMS; i++) {
char str[256] = {}, *key;
snprintf(str, sizeof(str), "%08zx", i);
snprintf(str, sizeof(str), "%04x", i);
key = isc_mem_strdup(mctx, str);
assert_non_null(key);
value.as_pointer = isc_mem_strdup(mctx, str);
assert_non_null(value.as_pointer);
result = isc_symtab_define(st, key, 1, value, policy);
result = isc_symtab_define(symtab, key, i + 1, value, policy);
assert_int_equal(result, ISC_R_SUCCESS);
if (result != ISC_R_SUCCESS) {
undefine(key, 1, value, NULL);
}
}
/*
* Try to put them in again; this should fail
*/
for (i = 0; i < 1024; i++) {
char str[16], *key;
snprintf(str, sizeof(str), "%04x", i);
key = isc_mem_strdup(mctx, str);
assert_non_null(key);
value.as_pointer = isc_mem_strdup(mctx, str);
assert_non_null(value.as_pointer);
result = isc_symtab_define(st, key, 1, value, policy);
assert_int_equal(result, ISC_R_EXISTS);
undefine(key, 1, value, NULL);
}
/*
* Retrieve them; this should succeed
*/
for (i = 0; i < 1024; i++) {
char str[16];
for (size_t i = 0; i < TEST_NITEMS; i++) {
char str[256] = {};
snprintf(str, sizeof(str), "%04x", i);
result = isc_symtab_lookup(st, str, 0, &value);
snprintf(str, sizeof(str), "%08zx", i);
result = isc_symtab_lookup(symtab, str, i + 1, &value);
assert_int_equal(result, ISC_R_SUCCESS);
assert_string_equal(str, (char *)value.as_pointer);
}
/*
* Undefine them
* Undefine even items them via foreach
*/
for (i = 0; i < 1024; i++) {
char str[16];
isc_symtab_foreach(symtab, peek, seen);
snprintf(str, sizeof(str), "%04x", i);
result = isc_symtab_undefine(st, str, 1);
assert_int_equal(result, ISC_R_SUCCESS);
for (size_t i = 0; i < TEST_NITEMS; i++) {
assert_true(seen[i]);
}
/*
* Retrieve them again; this should fail
* Destroy the even ones by hand.
*/
for (i = 0; i < 1024; i++) {
char str[16];
for (size_t i = 0; i < TEST_NITEMS; i++) {
if (i % 2 == 0) {
char str[256] = {};
snprintf(str, sizeof(str), "%04x", i);
result = isc_symtab_lookup(st, str, 0, &value);
assert_int_equal(result, ISC_R_NOTFOUND);
snprintf(str, sizeof(str), "%08zx", i);
result = isc_symtab_undefine(symtab, str, i + 1);
assert_int_equal(result, ISC_R_SUCCESS);
}
}
isc_symtab_destroy(&st);
isc_symtab_destroy(&symtab);
}
ISC_TEST_LIST_START
ISC_TEST_ENTRY(symtab_grow)
ISC_TEST_ENTRY(symtab_define)
ISC_TEST_ENTRY(symtab_undefine)
ISC_TEST_ENTRY(symtab_reject)
ISC_TEST_ENTRY(symtab_replace)
ISC_TEST_ENTRY(symtab_foreach)
ISC_TEST_LIST_END