Files
coven/modules/ufbx/misc/hash_collider.c

230 lines
5.1 KiB
C

#include "../ufbx.c"
#include <inttypes.h>
typedef struct {
uint32_t begin;
uint32_t increment;
uint64_t attempts;
uint32_t *slots;
size_t num_slots;
size_t target_slot;
} hash_info;
typedef void hash_fn(hash_info info);
typedef struct {
char str[256];
uint32_t *slots;
size_t mask;
size_t max_length;
int64_t attempts_left;
size_t target_slot;
} str_state;
ufbxi_noinline void print_string(str_state *state, size_t length)
{
#if _OPENMP
#pragma omp critical
#endif
{
state->str[length] = '\0';
puts(state->str);
}
}
ufbxi_noinline void hash_string_imp(str_state *state, size_t length)
{
if (state->attempts_left < 0) return;
state->attempts_left -= ('Z' - 'A' + 1) * 2;
uint32_t *slots = state->slots;
size_t mask = state->mask;
size_t target_slot = state->target_slot;
char *p = state->str + length - 1;
for (uint32_t c = 'A'; c <= 'Z'; c++) {
*p = c;
uint32_t hash = ufbxi_hash_string(state->str, length);
if ((hash & mask) == target_slot) print_string(state, length);
slots[hash & mask]++;
*p = c | 0x20;
hash = ufbxi_hash_string(state->str, length);
if ((hash & mask) == target_slot) print_string(state, length);
slots[hash & mask]++;
}
if (length < state->max_length) {
for (uint32_t c = 'A'; c <= 'Z'; c++) {
*p = c;
hash_string_imp(state, length + 1);
*p = c | 0x20;
hash_string_imp(state, length + 1);
}
}
}
ufbxi_noinline void hash_string(hash_info info)
{
size_t mask = info.num_slots - 1;
str_state state;
state.attempts_left = (int64_t)info.attempts;
state.mask = mask;
state.slots = info.slots;
state.target_slot = info.target_slot;
size_t max_len = 0;
uint64_t len_attempts = 1;
while (len_attempts < info.attempts) {
len_attempts *= ('Z' - 'A' + 1) * 2;
max_len += 1;
}
state.max_length = max_len;
for (uint32_t c = 'A' + info.begin; c <= 'Z'; c += info.increment) {
state.str[0] = c;
uint32_t hash = ufbxi_hash_string(state.str, 1);
if ((hash & mask) == info.target_slot) print_string(&state, 1);
info.slots[hash & mask]++;
if (max_len > 1) {
hash_string_imp(&state, 2);
}
state.str[0] = c | 0x20;
hash = ufbxi_hash_string(state.str, 1);
if ((hash & mask) == info.target_slot) print_string(&state, 1);
info.slots[hash & mask]++;
if (max_len > 1) {
hash_string_imp(&state, 2);
}
}
}
ufbxi_noinline void print_uint64(uint64_t v)
{
#if _OPENMP
#pragma omp critical
#endif
{
printf("%" PRIu64 "\n", v);
}
}
ufbxi_noinline void hash_uint64(hash_info info)
{
size_t mask = info.num_slots - 1;
uint64_t increment = info.increment;
uint64_t end = info.attempts;
for (uint64_t i = info.begin; i < end; i += increment) {
uint32_t hash = ufbxi_hash64(i);
if ((hash & mask) == info.target_slot) print_uint64(i);
info.slots[hash & mask]++;
}
}
ufbxi_noinline void run_test(hash_info info, hash_fn *fn, uint32_t *accumulator)
{
info.slots = malloc(sizeof(uint32_t) * info.num_slots);
memset(info.slots, 0, sizeof(uint32_t) * info.num_slots);
fn(info);
#if _OPENMP
#pragma omp critical
#endif
{
for (size_t i = 0; i < info.num_slots; i++) {
accumulator[i] += info.slots[i];
}
}
}
typedef struct {
const char *name;
hash_fn *fn;
} hash_test;
hash_test tests[] = {
"string", &hash_string,
"uint64", &hash_uint64,
};
int main(int argc, char **argv)
{
const char *func = "";
size_t num_slots = 0;
size_t target_slot = SIZE_MAX;
uint64_t attempts = 0;
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-f")) {
if (++i < argc) func = argv[i];
} else if (!strcmp(argv[i], "--slots")) {
if (++i < argc) num_slots = (size_t)strtoull(argv[i], NULL, 10);
} else if (!strcmp(argv[i], "--attempts")) {
if (++i < argc) attempts = (uint64_t)strtoull(argv[i], NULL, 10);
} else if (!strcmp(argv[i], "--target")) {
if (++i < argc) target_slot = (size_t)strtoull(argv[i], NULL, 10);
} else if (!strcmp(argv[i], "--threads")) {
#if _OPENMP
if (++i < argc) omp_set_num_threads(atoi(argv[i]));
#endif
}
}
hash_fn *hash_fn = NULL;
for (size_t i = 0; i < ufbxi_arraycount(tests); i++) {
if (!strcmp(tests[i].name, func)) {
hash_fn = tests[i].fn;
break;
}
}
if (!hash_fn) {
fprintf(stderr, "Unkonwn hash function '%s'\n", func);
return 1;
}
if ((num_slots & (num_slots - 1)) != 0) {
fprintf(stderr, "Slot amount must be a power of two, got %zu\n", num_slots);
return 1;
}
uint32_t *slots = malloc(num_slots * sizeof(uint32_t));
memset(slots, 0, sizeof(uint32_t) * num_slots);
hash_info info;
info.attempts = (int64_t)attempts;
info.increment = 1;
info.num_slots = num_slots;
info.target_slot = target_slot;
#if _OPENMP
#pragma omp parallel
{
info.begin = (uint64_t)omp_get_thread_num();
info.increment = (uint64_t)omp_get_num_threads();
run_test(info, hash_fn, slots);
}
#else
{
info.begin = 0;
info.increment = 1;
run_test(info, hash_fn, slots);
}
#endif
uint32_t max_collisions = 0;
size_t worst_slot = 0;
for (size_t i = 0; i < num_slots; i++) {
if (slots[i] > max_collisions) {
max_collisions = slots[i];
worst_slot = i;
}
}
if (target_slot == SIZE_MAX) {
printf("Worst slot: %zu (%u collisions)\n", worst_slot, max_collisions);
}
return 0;
}