Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion code/header/essentials/collections/hashtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ typedef struct zpl_hash_table_find_result {
zpl_isize entry_index;
} zpl_hash_table_find_result;

ZPL_IMPL_INLINE zpl_u64 zpl__hash_table_mix_key(zpl_u64 key) {
key ^= key >> 33;
key *= 0xff51afd7ed558ccdull;
key ^= key >> 33;
key *= 0xc4ceb9fe1a85ec53ull;
key ^= key >> 33;
return key;
}

/**
* Combined macro for a quick delcaration + definition
*/
Expand All @@ -60,6 +69,7 @@ typedef struct zpl_hash_table_find_result {
typedef struct NAME { \
zpl_array(zpl_isize) hashes; \
zpl_array(ZPL_JOIN2(NAME, Entry)) entries; \
zpl_u64 seed; \
} NAME; \
\
PREFIX void ZPL_JOIN2(FUNC, init) (NAME *h, zpl_allocator a); \
Expand All @@ -84,6 +94,9 @@ typedef struct zpl_hash_table_find_result {
void ZPL_JOIN2(FUNC, init)(NAME * h, zpl_allocator a) { \
zpl_array_init(h->hashes, a); \
zpl_array_init(h->entries, a); \
h->seed = zpl__hash_table_mix_key(cast(zpl_u64)cast(zpl_uintptr)h ^ \
cast(zpl_u64)cast(zpl_uintptr)h->hashes ^ \
cast(zpl_u64)cast(zpl_uintptr)h->entries); \
} \
\
void ZPL_JOIN2(FUNC, destroy)(NAME * h) { \
Expand Down Expand Up @@ -118,7 +131,8 @@ typedef struct zpl_hash_table_find_result {
zpl_internal zpl_hash_table_find_result ZPL_JOIN2(FUNC, _find)(NAME * h, zpl_u64 key) { \
zpl_hash_table_find_result r = { -1, -1, -1 }; \
if (zpl_array_count(h->hashes) > 0) { \
r.hash_index = key % zpl_array_count(h->hashes); \
r.hash_index = cast(zpl_isize)(zpl__hash_table_mix_key(key ^ h->seed) % \
cast(zpl_u64)zpl_array_count(h->hashes)); \
r.entry_index = h->hashes[r.hash_index]; \
while (r.entry_index >= 0) { \
if (h->entries[r.entry_index].key == key) return r; \
Expand All @@ -142,6 +156,7 @@ typedef struct zpl_hash_table_find_result {
zpl_isize i, j; \
NAME nh = { 0 }; \
ZPL_JOIN2(FUNC, init)(&nh, zpl_array_allocator(h->hashes)); \
nh.seed = h->seed; \
zpl_array_resize(nh.hashes, new_count); \
zpl_array_reserve(nh.entries, zpl_array_count(h->entries)); \
for (i = 0; i < new_count; i++) nh.hashes[i] = -1; \
Expand All @@ -162,6 +177,7 @@ typedef struct zpl_hash_table_find_result {
ZPL_JOIN2(FUNC, destroy)(h); \
h->hashes = nh.hashes; \
h->entries = nh.entries; \
h->seed = nh.seed; \
} \
\
void ZPL_JOIN2(FUNC, rehash_fast)(NAME * h) { \
Expand Down
24 changes: 24 additions & 0 deletions code/tests/cases/table.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,28 @@ MODULE(table, {

unit_table_destroy(&t1);
});

IT("should mix keys before bucket assignment", {
unit_table t1 = {0};
unit_table_init(&t1, zpl_heap());
t1.seed = 0;
unit_table_rehash(&t1, 64);

for (int i = 0; i < 16; ++i) {
unit_table_set(&t1, cast(zpl_u64)(i * 64), i);
}

zpl_isize bucket_zero_count = 0;
for (zpl_isize idx = t1.hashes[0]; idx >= 0; idx = t1.entries[idx].next) {
bucket_zero_count++;
}

LESSER(bucket_zero_count, 16);

for (int i = 0; i < 16; ++i) {
EQUALS(i, *unit_table_get(&t1, cast(zpl_u64)(i * 64)));
}

unit_table_destroy(&t1);
});
});