2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-24 02:58:38 +00:00
bind/tests/isc/hashmap_test.c

437 lines
11 KiB
C
Raw Normal View History

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <inttypes.h>
#include <sched.h> /* IWYU pragma: keep */
#include <setjmp.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define UNIT_TESTING
#include <cmocka.h>
#include <isc/hash.h>
#include <isc/hashmap.h>
#include <isc/mem.h>
#include <isc/string.h>
#include <isc/util.h>
#include <tests/isc.h>
/* INCLUDE LAST */
#define mctx __mctx
#include "hashmap.c"
#undef mctx
typedef struct test_node {
uint32_t hashval;
char key[64];
} test_node_t;
static void
test_hashmap_full(uint8_t init_bits, uintptr_t count) {
isc_hashmap_t *hashmap = NULL;
isc_result_t result;
test_node_t *nodes, *long_nodes, *upper_nodes;
nodes = isc_mem_get(mctx, count * sizeof(nodes[0]));
long_nodes = isc_mem_get(mctx, count * sizeof(nodes[0]));
upper_nodes = isc_mem_get(mctx, count * sizeof(nodes[0]));
isc_hashmap_create(mctx, init_bits, ISC_HASHMAP_CASE_SENSITIVE,
&hashmap);
assert_non_null(hashmap);
/*
* Note: snprintf() is followed with strlcat()
* to ensure we are always filling the 16 byte key.
*/
for (size_t i = 0; i < count; i++) {
/* short keys */
snprintf(nodes[i].key, 16, "%u", (unsigned int)i);
strlcat(nodes[i].key, " key of a raw hashmap!!", 16);
/* long keys */
snprintf(long_nodes[i].key, sizeof(long_nodes[i].key), "%u",
(unsigned int)i);
strlcat(long_nodes[i].key, " key of a raw hashmap!!",
sizeof(long_nodes[i].key));
/* (some) uppercase keys */
snprintf(upper_nodes[i].key, 16, "%u", (unsigned int)i);
strlcat(upper_nodes[i].key, " KEY of a raw hashmap!!", 16);
}
/* insert short nodes */
for (size_t i = 0; i < count; i++) {
nodes[i].hashval = isc_hashmap_hash(hashmap, nodes[i].key, 16);
result = isc_hashmap_add(hashmap, &(nodes[i]).hashval,
nodes[i].key, 16, &nodes[i]);
assert_int_equal(result, ISC_R_SUCCESS);
}
/* check if the short nodes were insert */
for (size_t i = 0; i < count; i++) {
void *f = NULL;
result = isc_hashmap_find(hashmap, &(nodes[i]).hashval,
nodes[i].key, 16, &f);
assert_int_equal(result, ISC_R_SUCCESS);
assert_ptr_equal(&nodes[i], f);
}
/* check for double inserts */
for (size_t i = 0; i < count; i++) {
result = isc_hashmap_add(hashmap, NULL, nodes[i].key, 16,
&nodes[i]);
assert_int_equal(result, ISC_R_EXISTS);
}
for (size_t i = 0; i < count; i++) {
result =
isc_hashmap_add(hashmap, NULL, long_nodes[i].key,
strlen((const char *)long_nodes[i].key),
&long_nodes[i]);
assert_int_equal(result, ISC_R_SUCCESS);
}
for (size_t i = 0; i < count; i++) {
void *f = NULL;
result = isc_hashmap_find(hashmap, NULL, upper_nodes[i].key, 16,
&f);
assert_int_equal(result, ISC_R_NOTFOUND);
assert_null(f);
}
for (size_t i = 0; i < count; i++) {
void *f = NULL;
result = isc_hashmap_find(
hashmap, NULL, long_nodes[i].key,
strlen((const char *)long_nodes[i].key), &f);
assert_int_equal(result, ISC_R_SUCCESS);
assert_ptr_equal(f, &long_nodes[i]);
}
for (size_t i = 0; i < count; i++) {
void *f = NULL;
result = isc_hashmap_delete(hashmap, &nodes[i].hashval,
nodes[i].key, 16);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_hashmap_find(hashmap, NULL, nodes[i].key, 16, &f);
assert_int_equal(result, ISC_R_NOTFOUND);
assert_null(f);
}
for (size_t i = 0; i < count; i++) {
result = isc_hashmap_add(hashmap, NULL, upper_nodes[i].key, 16,
&upper_nodes[i]);
assert_int_equal(result, ISC_R_SUCCESS);
}
for (size_t i = 0; i < count; i++) {
void *f = NULL;
result = isc_hashmap_delete(
hashmap, NULL, long_nodes[i].key,
strlen((const char *)long_nodes[i].key));
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_hashmap_find(
hashmap, NULL, long_nodes[i].key,
strlen((const char *)long_nodes[i].key), &f);
assert_int_equal(result, ISC_R_NOTFOUND);
assert_null(f);
}
for (size_t i = 0; i < count; i++) {
void *f = NULL;
result = isc_hashmap_find(hashmap, NULL, upper_nodes[i].key, 16,
&f);
assert_int_equal(result, ISC_R_SUCCESS);
assert_ptr_equal(f, &upper_nodes[i]);
}
for (size_t i = 0; i < count; i++) {
void *f = NULL;
result = isc_hashmap_find(hashmap, NULL, nodes[i].key, 16, &f);
assert_int_equal(result, ISC_R_NOTFOUND);
assert_null(f);
}
isc_hashmap_destroy(&hashmap);
assert_null(hashmap);
isc_mem_put(mctx, nodes, count * sizeof(nodes[0]));
isc_mem_put(mctx, long_nodes, count * sizeof(nodes[0]));
isc_mem_put(mctx, upper_nodes, count * sizeof(nodes[0]));
}
static void
test_hashmap_iterator(void) {
isc_hashmap_t *hashmap = NULL;
isc_result_t result;
isc_hashmap_iter_t *iter = NULL;
size_t count = 7600;
uint32_t walked;
size_t tksize;
test_node_t *nodes;
nodes = isc_mem_get(mctx, count * sizeof(nodes[0]));
isc_hashmap_create(mctx, HASHMAP_MIN_BITS, ISC_HASHMAP_CASE_SENSITIVE,
&hashmap);
assert_non_null(hashmap);
for (size_t i = 0; i < count; i++) {
/* short keys */
snprintf(nodes[i].key, 16, "%u", (unsigned int)i);
strlcat(nodes[i].key, " key of a raw hashmap!!", 16);
}
for (size_t i = 0; i < count; i++) {
result = isc_hashmap_add(hashmap, NULL, nodes[i].key, 16,
&nodes[i]);
assert_int_equal(result, ISC_R_SUCCESS);
}
/* We want to iterate while rehashing is in progress */
assert_true(rehashing_in_progress(hashmap));
walked = 0;
isc_hashmap_iter_create(hashmap, &iter);
for (result = isc_hashmap_iter_first(iter); result == ISC_R_SUCCESS;
result = isc_hashmap_iter_next(iter))
{
char key[16] = { 0 };
ptrdiff_t i;
const uint8_t *tkey = NULL;
test_node_t *v = NULL;
isc_hashmap_iter_current(iter, (void *)&v);
isc_hashmap_iter_currentkey(iter, &tkey, &tksize);
assert_int_equal(tksize, 16);
i = v - &nodes[0];
snprintf(key, 16, "%u", (unsigned int)i);
strlcat(key, " key of a raw hashmap!!", 16);
assert_memory_equal(key, tkey, 16);
walked++;
}
assert_int_equal(walked, count);
assert_int_equal(result, ISC_R_NOMORE);
/* erase odd */
walked = 0;
result = isc_hashmap_iter_first(iter);
while (result == ISC_R_SUCCESS) {
char key[16] = { 0 };
ptrdiff_t i;
const uint8_t *tkey = NULL;
test_node_t *v = NULL;
isc_hashmap_iter_current(iter, (void *)&v);
isc_hashmap_iter_currentkey(iter, &tkey, &tksize);
assert_int_equal(tksize, 16);
i = v - nodes;
snprintf(key, 16, "%u", (unsigned int)i);
strlcat(key, " key of a raw hashmap!!", 16);
assert_memory_equal(key, tkey, 16);
if (i % 2 == 0) {
result = isc_hashmap_iter_delcurrent_next(iter);
} else {
result = isc_hashmap_iter_next(iter);
}
walked++;
}
assert_int_equal(result, ISC_R_NOMORE);
assert_int_equal(walked, count);
/* erase even */
walked = 0;
result = isc_hashmap_iter_first(iter);
while (result == ISC_R_SUCCESS) {
char key[16] = { 0 };
ptrdiff_t i;
const uint8_t *tkey = NULL;
test_node_t *v = NULL;
isc_hashmap_iter_current(iter, (void *)&v);
isc_hashmap_iter_currentkey(iter, &tkey, &tksize);
assert_int_equal(tksize, 16);
i = v - nodes;
snprintf(key, 16, "%u", (unsigned int)i);
strlcat(key, " key of a raw hashmap!!", 16);
assert_memory_equal(key, tkey, 16);
if (i % 2 == 1) {
result = isc_hashmap_iter_delcurrent_next(iter);
} else {
result = isc_hashmap_iter_next(iter);
}
walked++;
}
assert_int_equal(result, ISC_R_NOMORE);
assert_int_equal(walked, count / 2);
walked = 0;
for (result = isc_hashmap_iter_first(iter); result == ISC_R_SUCCESS;
result = isc_hashmap_iter_next(iter))
{
walked++;
}
assert_int_equal(result, ISC_R_NOMORE);
assert_int_equal(walked, 0);
/* Iterator doesn't progress rehashing */
assert_true(rehashing_in_progress(hashmap));
isc_hashmap_iter_destroy(&iter);
assert_null(iter);
isc_hashmap_destroy(&hashmap);
assert_null(hashmap);
isc_mem_put(mctx, nodes, count * sizeof(nodes[0]));
}
/* 1 bit, 120 elements test, full rehashing */
ISC_RUN_TEST_IMPL(isc_hashmap_1_120) {
test_hashmap_full(1, 120);
return;
}
/* 6 bit, 1000 elements test, full rehashing */
ISC_RUN_TEST_IMPL(isc_hashmap_6_1000) {
test_hashmap_full(6, 1000);
return;
}
/* 24 bit, 200K elements test, no rehashing */
ISC_RUN_TEST_IMPL(isc_hashmap_24_200000) {
test_hashmap_full(24, 200000);
return;
}
/* 15 bit, 45K elements test, full rehashing */
ISC_RUN_TEST_IMPL(isc_hashmap_1_48000) {
test_hashmap_full(1, 48000);
return;
}
/* 8 bit, 20k elements test, partial rehashing */
ISC_RUN_TEST_IMPL(isc_hashmap_8_20000) {
test_hashmap_full(8, 20000);
return;
}
/* test hashmap iterator */
ISC_RUN_TEST_IMPL(isc_hashmap_iterator) {
test_hashmap_iterator();
return;
}
ISC_RUN_TEST_IMPL(isc_hashmap_hash_zero_length) {
isc_hashmap_t *hashmap = NULL;
uint32_t hashval;
bool again = false;
again:
isc_hashmap_create(mctx, 1, ISC_HASHMAP_CASE_SENSITIVE, &hashmap);
hashval = isc_hashmap_hash(hashmap, "", 0);
isc_hashmap_destroy(&hashmap);
if (hashval == 0 && !again) {
/*
* We could be extremely unlock and the siphash could hash the
* zero length string to 0, so try one more time.
*/
again = true;
goto again;
}
assert_int_not_equal(hashval, 0);
}
ISC_RUN_TEST_IMPL(isc_hashmap_case) {
isc_result_t result;
isc_hashmap_t *hashmap = NULL;
test_node_t lower = { .key = "isc_hashmap_case" };
test_node_t upper = { .key = "ISC_HASHMAP_CASE" };
test_node_t mixed = { .key = "IsC_hAsHmAp_CaSe" };
isc_hashmap_create(mctx, 1, ISC_HASHMAP_CASE_SENSITIVE, &hashmap);
result = isc_hashmap_add(hashmap, NULL, lower.key, strlen(lower.key),
&lower);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_hashmap_add(hashmap, NULL, lower.key, strlen(lower.key),
&lower);
assert_int_equal(result, ISC_R_EXISTS);
result = isc_hashmap_add(hashmap, NULL, upper.key, strlen(upper.key),
&upper);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_hashmap_find(hashmap, NULL, mixed.key, strlen(mixed.key),
&(void *){ NULL });
assert_int_equal(result, ISC_R_NOTFOUND);
isc_hashmap_destroy(&hashmap);
isc_hashmap_create(mctx, 1, ISC_HASHMAP_CASE_INSENSITIVE, &hashmap);
result = isc_hashmap_add(hashmap, NULL, lower.key, strlen(lower.key),
&lower);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_hashmap_add(hashmap, NULL, lower.key, strlen(lower.key),
&lower);
assert_int_equal(result, ISC_R_EXISTS);
result = isc_hashmap_add(hashmap, NULL, upper.key, strlen(upper.key),
&upper);
assert_int_equal(result, ISC_R_EXISTS);
result = isc_hashmap_find(hashmap, NULL, mixed.key, strlen(mixed.key),
&(void *){ NULL });
assert_int_equal(result, ISC_R_SUCCESS);
isc_hashmap_destroy(&hashmap);
}
ISC_TEST_LIST_START
ISC_TEST_ENTRY(isc_hashmap_hash_zero_length)
ISC_TEST_ENTRY(isc_hashmap_case)
ISC_TEST_ENTRY(isc_hashmap_1_120)
ISC_TEST_ENTRY(isc_hashmap_6_1000)
ISC_TEST_ENTRY(isc_hashmap_24_200000)
ISC_TEST_ENTRY(isc_hashmap_1_48000)
ISC_TEST_ENTRY(isc_hashmap_8_20000)
ISC_TEST_ENTRY(isc_hashmap_iterator)
ISC_TEST_LIST_END
ISC_TEST_MAIN