1 1.1 christos /* $NetBSD: ht_test.c,v 1.3 2025/01/26 16:25:49 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.1 christos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 1.1 christos * 6 1.1 christos * SPDX-License-Identifier: MPL-2.0 7 1.1 christos * 8 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 9 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 10 1.1 christos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 1.1 christos * 12 1.1 christos * See the COPYRIGHT file distributed with this work for additional 13 1.1 christos * information regarding copyright ownership. 14 1.1 christos */ 15 1.1 christos 16 1.1 christos #include <inttypes.h> 17 1.1 christos #include <sched.h> /* IWYU pragma: keep */ 18 1.1 christos #include <setjmp.h> 19 1.1 christos #include <stdarg.h> 20 1.1 christos #include <stddef.h> 21 1.1 christos #include <stdio.h> 22 1.1 christos #include <stdlib.h> 23 1.1 christos #include <string.h> 24 1.1 christos 25 1.1 christos #define UNIT_TESTING 26 1.1 christos #include <cmocka.h> 27 1.1 christos 28 1.1 christos #include <isc/hash.h> 29 1.1 christos #include <isc/ht.h> 30 1.1 christos #include <isc/mem.h> 31 1.1 christos #include <isc/string.h> 32 1.1 christos #include <isc/util.h> 33 1.1 christos 34 1.1 christos #include <tests/isc.h> 35 1.1 christos 36 1.1 christos /* INCLUDE LAST */ 37 1.1 christos 38 1.1 christos #define mctx __mctx 39 1.1 christos #include "ht.c" 40 1.1 christos #undef mctx 41 1.1 christos 42 1.1 christos static void 43 1.3 christos test_ht_full(uint8_t init_bits, uintptr_t count) { 44 1.1 christos isc_ht_t *ht = NULL; 45 1.1 christos isc_result_t result; 46 1.1 christos uintptr_t i; 47 1.1 christos 48 1.3 christos isc_ht_init(&ht, mctx, init_bits, ISC_HT_CASE_SENSITIVE); 49 1.1 christos assert_non_null(ht); 50 1.1 christos 51 1.1 christos for (i = 1; i < count; i++) { 52 1.1 christos /* 53 1.1 christos * Note: snprintf() is followed with strlcat() 54 1.1 christos * to ensure we are always filling the 16 byte key. 55 1.1 christos */ 56 1.1 christos unsigned char key[16]; 57 1.1 christos snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); 58 1.1 christos strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); 59 1.1 christos result = isc_ht_add(ht, key, 16, (void *)i); 60 1.1 christos assert_int_equal(result, ISC_R_SUCCESS); 61 1.1 christos } 62 1.1 christos 63 1.1 christos for (i = 1; i < count; i++) { 64 1.1 christos unsigned char key[16]; 65 1.1 christos void *f = NULL; 66 1.1 christos snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); 67 1.1 christos strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); 68 1.1 christos result = isc_ht_find(ht, key, 16, &f); 69 1.1 christos assert_int_equal(result, ISC_R_SUCCESS); 70 1.1 christos assert_ptr_equal((void *)i, (void *)f); 71 1.1 christos } 72 1.1 christos 73 1.1 christos for (i = 1; i < count; i++) { 74 1.1 christos unsigned char key[16]; 75 1.1 christos snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); 76 1.1 christos strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); 77 1.1 christos result = isc_ht_add(ht, key, 16, (void *)i); 78 1.1 christos assert_int_equal(result, ISC_R_EXISTS); 79 1.1 christos } 80 1.1 christos 81 1.1 christos for (i = 1; i < count; i++) { 82 1.1 christos char key[64]; 83 1.1 christos /* 84 1.1 christos * Note: the key size is now strlen(key) which is bigger 85 1.1 christos * then the keys added above. 86 1.1 christos */ 87 1.1 christos snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); 88 1.1 christos strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); 89 1.1 christos result = isc_ht_add(ht, (const unsigned char *)key, strlen(key), 90 1.1 christos (void *)i); 91 1.1 christos assert_int_equal(result, ISC_R_SUCCESS); 92 1.1 christos } 93 1.1 christos 94 1.1 christos for (i = 1; i < count; i++) { 95 1.1 christos unsigned char key[16]; 96 1.1 christos void *f = NULL; 97 1.1 christos /* 98 1.1 christos * Note: case of KEY is now in capitals, 99 1.1 christos */ 100 1.1 christos snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); 101 1.1 christos strlcat((char *)key, " KEY of a raw hashtable!!", sizeof(key)); 102 1.1 christos result = isc_ht_find(ht, key, 16, &f); 103 1.1 christos assert_int_equal(result, ISC_R_NOTFOUND); 104 1.1 christos assert_null(f); 105 1.1 christos } 106 1.1 christos 107 1.1 christos for (i = 1; i < count; i++) { 108 1.1 christos char key[64]; 109 1.1 christos void *f = NULL; 110 1.1 christos snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); 111 1.1 christos strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); 112 1.1 christos result = isc_ht_find(ht, (const unsigned char *)key, 113 1.1 christos strlen(key), &f); 114 1.1 christos assert_int_equal(result, ISC_R_SUCCESS); 115 1.1 christos assert_ptr_equal(f, (void *)i); 116 1.1 christos } 117 1.1 christos 118 1.1 christos for (i = 1; i < count; i++) { 119 1.1 christos unsigned char key[16]; 120 1.1 christos void *f = NULL; 121 1.1 christos snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); 122 1.1 christos strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); 123 1.1 christos result = isc_ht_delete(ht, key, 16); 124 1.1 christos assert_int_equal(result, ISC_R_SUCCESS); 125 1.1 christos result = isc_ht_find(ht, key, 16, &f); 126 1.1 christos assert_int_equal(result, ISC_R_NOTFOUND); 127 1.1 christos assert_null(f); 128 1.1 christos } 129 1.1 christos 130 1.1 christos for (i = 1; i < count; i++) { 131 1.1 christos unsigned char key[16]; 132 1.1 christos /* 133 1.1 christos * Note: upper case KEY. 134 1.1 christos */ 135 1.1 christos snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); 136 1.1 christos strlcat((char *)key, " KEY of a raw hashtable!!", sizeof(key)); 137 1.1 christos result = isc_ht_add(ht, key, 16, (void *)i); 138 1.1 christos assert_int_equal(result, ISC_R_SUCCESS); 139 1.1 christos } 140 1.1 christos 141 1.1 christos for (i = 1; i < count; i++) { 142 1.1 christos char key[64]; 143 1.1 christos void *f = NULL; 144 1.1 christos snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); 145 1.1 christos strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); 146 1.1 christos result = isc_ht_delete(ht, (const unsigned char *)key, 147 1.1 christos strlen(key)); 148 1.1 christos assert_int_equal(result, ISC_R_SUCCESS); 149 1.1 christos result = isc_ht_find(ht, (const unsigned char *)key, 150 1.1 christos strlen(key), &f); 151 1.1 christos assert_int_equal(result, ISC_R_NOTFOUND); 152 1.1 christos assert_null(f); 153 1.1 christos } 154 1.1 christos 155 1.1 christos for (i = 1; i < count; i++) { 156 1.1 christos unsigned char key[16]; 157 1.1 christos void *f = NULL; 158 1.1 christos /* 159 1.1 christos * Note: case of KEY is now in capitals, 160 1.1 christos */ 161 1.1 christos snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); 162 1.1 christos strlcat((char *)key, " KEY of a raw hashtable!!", sizeof(key)); 163 1.1 christos result = isc_ht_find(ht, key, 16, &f); 164 1.1 christos assert_int_equal(result, ISC_R_SUCCESS); 165 1.1 christos assert_ptr_equal((void *)i, (void *)f); 166 1.1 christos } 167 1.1 christos 168 1.1 christos for (i = 1; i < count; i++) { 169 1.1 christos unsigned char key[16]; 170 1.1 christos void *f = NULL; 171 1.1 christos snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); 172 1.1 christos strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); 173 1.1 christos result = isc_ht_find(ht, key, 16, &f); 174 1.1 christos assert_int_equal(result, ISC_R_NOTFOUND); 175 1.1 christos assert_null(f); 176 1.1 christos } 177 1.1 christos 178 1.1 christos isc_ht_destroy(&ht); 179 1.1 christos assert_null(ht); 180 1.1 christos } 181 1.1 christos 182 1.1 christos static void 183 1.1 christos test_ht_iterator(void) { 184 1.1 christos isc_ht_t *ht = NULL; 185 1.1 christos isc_result_t result; 186 1.1 christos isc_ht_iter_t *iter = NULL; 187 1.1 christos uintptr_t i; 188 1.3 christos uintptr_t count = 7600; 189 1.1 christos uint32_t walked; 190 1.1 christos unsigned char key[16]; 191 1.1 christos size_t tksize; 192 1.1 christos 193 1.3 christos isc_ht_init(&ht, mctx, HT_MIN_BITS, ISC_HT_CASE_SENSITIVE); 194 1.1 christos assert_non_null(ht); 195 1.1 christos for (i = 1; i <= count; i++) { 196 1.1 christos /* 197 1.1 christos * Note that the string we're snprintfing is always > 16 bytes 198 1.1 christos * so we are always filling the key. 199 1.1 christos */ 200 1.1 christos snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); 201 1.1 christos strlcat((char *)key, "key of a raw hashtable!!", sizeof(key)); 202 1.1 christos result = isc_ht_add(ht, key, 16, (void *)i); 203 1.1 christos assert_int_equal(result, ISC_R_SUCCESS); 204 1.1 christos } 205 1.1 christos 206 1.3 christos /* We want to iterate while rehashing is in progress */ 207 1.3 christos assert_true(rehashing_in_progress(ht)); 208 1.3 christos 209 1.1 christos walked = 0; 210 1.1 christos isc_ht_iter_create(ht, &iter); 211 1.1 christos 212 1.1 christos for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS; 213 1.1 christos result = isc_ht_iter_next(iter)) 214 1.1 christos { 215 1.1 christos unsigned char *tkey = NULL; 216 1.1 christos void *v = NULL; 217 1.1 christos 218 1.1 christos isc_ht_iter_current(iter, &v); 219 1.1 christos isc_ht_iter_currentkey(iter, &tkey, &tksize); 220 1.1 christos assert_int_equal(tksize, 16); 221 1.1 christos i = (uintptr_t)v; 222 1.1 christos snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); 223 1.1 christos strlcat((char *)key, "key of a raw hashtable!!", sizeof(key)); 224 1.1 christos assert_memory_equal(key, tkey, 16); 225 1.1 christos walked++; 226 1.1 christos } 227 1.1 christos assert_int_equal(walked, count); 228 1.1 christos assert_int_equal(result, ISC_R_NOMORE); 229 1.1 christos 230 1.1 christos /* erase odd */ 231 1.1 christos walked = 0; 232 1.1 christos result = isc_ht_iter_first(iter); 233 1.1 christos while (result == ISC_R_SUCCESS) { 234 1.1 christos unsigned char *tkey = NULL; 235 1.1 christos void *v = NULL; 236 1.1 christos 237 1.1 christos isc_ht_iter_current(iter, &v); 238 1.1 christos isc_ht_iter_currentkey(iter, &tkey, &tksize); 239 1.1 christos assert_int_equal(tksize, 16); 240 1.1 christos i = (uintptr_t)v; 241 1.1 christos snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); 242 1.1 christos strlcat((char *)key, "key of a raw hashtable!!", sizeof(key)); 243 1.1 christos assert_memory_equal(key, tkey, 16); 244 1.1 christos if ((uintptr_t)v % 2 == 0) { 245 1.1 christos result = isc_ht_iter_delcurrent_next(iter); 246 1.1 christos } else { 247 1.1 christos result = isc_ht_iter_next(iter); 248 1.1 christos } 249 1.1 christos walked++; 250 1.1 christos } 251 1.1 christos assert_int_equal(result, ISC_R_NOMORE); 252 1.1 christos assert_int_equal(walked, count); 253 1.1 christos 254 1.1 christos /* erase even */ 255 1.1 christos walked = 0; 256 1.1 christos result = isc_ht_iter_first(iter); 257 1.1 christos while (result == ISC_R_SUCCESS) { 258 1.1 christos unsigned char *tkey = NULL; 259 1.1 christos void *v = NULL; 260 1.1 christos 261 1.1 christos isc_ht_iter_current(iter, &v); 262 1.1 christos isc_ht_iter_currentkey(iter, &tkey, &tksize); 263 1.1 christos assert_int_equal(tksize, 16); 264 1.1 christos i = (uintptr_t)v; 265 1.1 christos snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); 266 1.1 christos strlcat((char *)key, "key of a raw hashtable!!", sizeof(key)); 267 1.1 christos assert_memory_equal(key, tkey, 16); 268 1.1 christos if ((uintptr_t)v % 2 == 1) { 269 1.1 christos result = isc_ht_iter_delcurrent_next(iter); 270 1.1 christos } else { 271 1.1 christos result = isc_ht_iter_next(iter); 272 1.1 christos } 273 1.1 christos walked++; 274 1.1 christos } 275 1.1 christos assert_int_equal(result, ISC_R_NOMORE); 276 1.1 christos assert_int_equal(walked, count / 2); 277 1.1 christos 278 1.1 christos walked = 0; 279 1.1 christos for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS; 280 1.1 christos result = isc_ht_iter_next(iter)) 281 1.1 christos { 282 1.1 christos walked++; 283 1.1 christos } 284 1.1 christos 285 1.1 christos assert_int_equal(result, ISC_R_NOMORE); 286 1.1 christos assert_int_equal(walked, 0); 287 1.1 christos 288 1.3 christos /* Iterator doesn't progress rehashing */ 289 1.3 christos assert_true(rehashing_in_progress(ht)); 290 1.3 christos 291 1.1 christos isc_ht_iter_destroy(&iter); 292 1.1 christos assert_null(iter); 293 1.1 christos 294 1.1 christos isc_ht_destroy(&ht); 295 1.1 christos assert_null(ht); 296 1.1 christos } 297 1.1 christos 298 1.3 christos /* 1 bit, 120 elements test, full rehashing */ 299 1.3 christos ISC_RUN_TEST_IMPL(isc_ht_1_120) { 300 1.3 christos test_ht_full(1, 120); 301 1.3 christos return; 302 1.3 christos } 303 1.3 christos 304 1.3 christos /* 6 bit, 1000 elements test, full rehashing */ 305 1.3 christos ISC_RUN_TEST_IMPL(isc_ht_6_1000) { 306 1.3 christos test_ht_full(6, 1000); 307 1.3 christos return; 308 1.3 christos } 309 1.3 christos 310 1.3 christos /* 24 bit, 200K elements test, no rehashing */ 311 1.3 christos ISC_RUN_TEST_IMPL(isc_ht_24_200000) { 312 1.1 christos UNUSED(state); 313 1.3 christos test_ht_full(24, 200000); 314 1.1 christos } 315 1.1 christos 316 1.3 christos /* 15 bit, 45K elements test, full rehashing */ 317 1.3 christos ISC_RUN_TEST_IMPL(isc_ht_1_48000) { 318 1.1 christos UNUSED(state); 319 1.3 christos test_ht_full(1, 48000); 320 1.1 christos } 321 1.1 christos 322 1.3 christos /* 8 bit, 20k elements test, partial rehashing */ 323 1.3 christos ISC_RUN_TEST_IMPL(isc_ht_8_20000) { 324 1.1 christos UNUSED(state); 325 1.3 christos test_ht_full(8, 20000); 326 1.1 christos } 327 1.1 christos 328 1.1 christos /* test hashtable iterator */ 329 1.1 christos 330 1.1 christos ISC_RUN_TEST_IMPL(isc_ht_iterator) { 331 1.1 christos UNUSED(state); 332 1.1 christos test_ht_iterator(); 333 1.1 christos } 334 1.1 christos 335 1.1 christos ISC_RUN_TEST_IMPL(isc_ht_case) { 336 1.1 christos isc_ht_t *ht = NULL; 337 1.1 christos void *f = NULL; 338 1.1 christos isc_result_t result = ISC_R_UNSET; 339 1.1 christos 340 1.1 christos unsigned char lower[16] = { "test case" }; 341 1.1 christos unsigned char same[16] = { "test case" }; 342 1.1 christos unsigned char upper[16] = { "TEST CASE" }; 343 1.1 christos unsigned char mixed[16] = { "tEsT CaSe" }; 344 1.1 christos 345 1.1 christos isc_ht_init(&ht, mctx, 8, ISC_HT_CASE_SENSITIVE); 346 1.1 christos assert_non_null(ht); 347 1.1 christos 348 1.1 christos result = isc_ht_add(ht, lower, 16, (void *)lower); 349 1.1 christos assert_int_equal(result, ISC_R_SUCCESS); 350 1.1 christos 351 1.1 christos result = isc_ht_add(ht, same, 16, (void *)same); 352 1.1 christos assert_int_equal(result, ISC_R_EXISTS); 353 1.1 christos 354 1.1 christos result = isc_ht_add(ht, upper, 16, (void *)upper); 355 1.1 christos assert_int_equal(result, ISC_R_SUCCESS); 356 1.1 christos 357 1.1 christos result = isc_ht_find(ht, mixed, 16, &f); 358 1.1 christos assert_int_equal(result, ISC_R_NOTFOUND); 359 1.1 christos assert_null(f); 360 1.1 christos 361 1.1 christos isc_ht_destroy(&ht); 362 1.1 christos assert_null(ht); 363 1.1 christos 364 1.1 christos isc_ht_init(&ht, mctx, 8, ISC_HT_CASE_INSENSITIVE); 365 1.1 christos assert_non_null(ht); 366 1.1 christos 367 1.1 christos result = isc_ht_add(ht, lower, 16, (void *)lower); 368 1.1 christos assert_int_equal(result, ISC_R_SUCCESS); 369 1.1 christos 370 1.1 christos result = isc_ht_add(ht, same, 16, (void *)same); 371 1.1 christos assert_int_equal(result, ISC_R_EXISTS); 372 1.1 christos 373 1.1 christos result = isc_ht_add(ht, upper, 16, (void *)upper); 374 1.1 christos assert_int_equal(result, ISC_R_EXISTS); 375 1.1 christos 376 1.1 christos result = isc_ht_find(ht, mixed, 16, &f); 377 1.1 christos assert_int_equal(result, ISC_R_SUCCESS); 378 1.1 christos assert_ptr_equal(f, &lower); 379 1.1 christos 380 1.1 christos isc_ht_destroy(&ht); 381 1.1 christos assert_null(ht); 382 1.1 christos } 383 1.1 christos 384 1.1 christos ISC_TEST_LIST_START 385 1.1 christos ISC_TEST_ENTRY(isc_ht_case) 386 1.3 christos ISC_TEST_ENTRY(isc_ht_1_120) 387 1.3 christos ISC_TEST_ENTRY(isc_ht_6_1000) 388 1.3 christos ISC_TEST_ENTRY(isc_ht_24_200000) 389 1.3 christos ISC_TEST_ENTRY(isc_ht_1_48000) 390 1.3 christos ISC_TEST_ENTRY(isc_ht_8_20000) 391 1.1 christos ISC_TEST_ENTRY(isc_ht_iterator) 392 1.1 christos ISC_TEST_LIST_END 393 1.1 christos 394 1.1 christos ISC_TEST_MAIN 395