mirror of
https://git.sr.ht/~cismonx/bookmarkfs
synced 2025-06-07 19:58:50 +00:00
- Move common defs and helper functions to check_util.h/check_utils.c. - Add a prepare script argument for ATX_CHECK_FS* macros. - ASSERT_EXPR_INT(): Use long int as expression result type. - Other misc updates.
206 lines
4.8 KiB
C
206 lines
4.8 KiB
C
/**
|
|
* bookmarkfs/tests/check_hashmap.c
|
|
* ----
|
|
*
|
|
* Copyright (C) 2025 CismonX <admin@cismon.net>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <sys/mman.h>
|
|
#include <unistd.h>
|
|
|
|
#include "check_util.h"
|
|
#include "frontend_util.h"
|
|
#include "hashmap.h"
|
|
#include "prng.h"
|
|
|
|
struct check_item {
|
|
unsigned long id;
|
|
unsigned long val;
|
|
};
|
|
|
|
// Forward declaration start
|
|
static int check_one_round (struct hashmap *, struct check_item *, size_t);
|
|
static int do_check_hashmap (size_t, int);
|
|
static int item_comp_func (union hashmap_key, void const *);
|
|
static unsigned long
|
|
item_hash_func (void const *);
|
|
static void item_walk_func (void *, void *);
|
|
// Forward declaration end
|
|
|
|
static int
|
|
check_one_round (
|
|
struct hashmap *map,
|
|
struct check_item *items,
|
|
size_t items_cnt
|
|
) {
|
|
for (size_t i = 0; i < items_cnt; ++i) {
|
|
struct check_item *item = items + (prng_rand() % items_cnt);
|
|
unsigned long id = item->id;
|
|
unsigned long val = item->val;
|
|
|
|
union hashmap_key key = { .u64 = id };
|
|
unsigned long entry_id;
|
|
void *found = hashmap_search(map, key, val, &entry_id);
|
|
|
|
if (id & 1) {
|
|
if (found != item) {
|
|
log_puts("unexpected item in hashmap");
|
|
return -1;
|
|
}
|
|
hashmap_delete(map, item, entry_id);
|
|
} else {
|
|
if (found != NULL) {
|
|
log_puts("unexpected item in hashmap");
|
|
return -1;
|
|
}
|
|
hashmap_insert(map, val, item);
|
|
}
|
|
item->id = id ^ 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
do_check_hashmap (
|
|
size_t items_cnt,
|
|
int rounds
|
|
) {
|
|
size_t buf_size = sizeof(struct check_item) * items_cnt;
|
|
struct check_item *items = mmap(NULL, buf_size, PROT_READ | PROT_WRITE,
|
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
if (items == MAP_FAILED) {
|
|
log_puts("failed to allocate memory");
|
|
return -1;
|
|
}
|
|
struct hashmap *map = hashmap_create(item_comp_func, item_hash_func);
|
|
|
|
for (size_t i = 0; i < items_cnt; ++i) {
|
|
items[i] = (struct check_item) {
|
|
.id = i << 1,
|
|
.val = prng_rand(),
|
|
};
|
|
}
|
|
|
|
int status = -1;
|
|
for (int i = 0; i < rounds; ++i) {
|
|
if (0 != check_one_round(map, items, items_cnt)) {
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
size_t cnt = 0;
|
|
hashmap_foreach(map, item_walk_func, &cnt);
|
|
|
|
for (size_t i = 0; i < items_cnt; ++i) {
|
|
struct check_item *item = items + i;
|
|
|
|
if (item->id & 1) {
|
|
--cnt;
|
|
hashmap_delete(map, item, -1);
|
|
}
|
|
}
|
|
if (cnt != 0) {
|
|
log_printf("bad item cnt %zu, expected 0", cnt);
|
|
goto end;
|
|
}
|
|
|
|
hashmap_foreach(map, item_walk_func, &cnt);
|
|
if (cnt != 0) {
|
|
log_printf("bad item cnt %zu, expected 0", cnt);
|
|
goto end;
|
|
}
|
|
status = 0;
|
|
|
|
end:
|
|
munmap(items, buf_size);
|
|
hashmap_destroy(map);
|
|
return status;
|
|
}
|
|
|
|
static int
|
|
item_comp_func (
|
|
union hashmap_key key,
|
|
void const *entry
|
|
) {
|
|
struct check_item const *item = entry;
|
|
|
|
return (key.u64 >> 1) - (item->id >> 1);
|
|
}
|
|
|
|
static unsigned long
|
|
item_hash_func (
|
|
void const *entry
|
|
) {
|
|
struct check_item const *item = entry;
|
|
|
|
return item->val;
|
|
}
|
|
|
|
static void
|
|
item_walk_func (
|
|
void *user_data,
|
|
void *UNUSED_VAR(entry)
|
|
) {
|
|
size_t *cnt_ptr = user_data;
|
|
|
|
++(*cnt_ptr);
|
|
}
|
|
|
|
int
|
|
check_hashmap (
|
|
int argc,
|
|
char *argv[]
|
|
) {
|
|
uint64_t seed_buf[4], *seed = NULL;
|
|
int size_exp = -1;
|
|
int rounds = -1;
|
|
|
|
OPT_START(argc, argv, "s:n:r:")
|
|
OPT_OPT('s') {
|
|
if (0 != prng_seed_from_hex(seed_buf, optarg)) {
|
|
return -1;
|
|
}
|
|
seed = seed_buf;
|
|
break;
|
|
}
|
|
OPT_OPT('n') {
|
|
size_exp = atoi(optarg);
|
|
break;
|
|
}
|
|
OPT_OPT('r') {
|
|
rounds = atoi(optarg);
|
|
break;
|
|
}
|
|
OPT_END
|
|
|
|
if (size_exp < 10 || size_exp > 30) {
|
|
log_printf("bad size %d", size_exp);
|
|
return -1;
|
|
}
|
|
if (rounds < 0) {
|
|
log_printf("bad rounds cnt %d", rounds);
|
|
return -1;
|
|
}
|
|
|
|
if (0 != prng_seed(seed)) {
|
|
return -1;
|
|
}
|
|
return do_check_hashmap(1u << size_exp, rounds);
|
|
}
|