power_play/src/base/base_util.h
2025-07-29 18:12:41 -05:00

248 lines
6.7 KiB
C

/* Utility functions and stuff that don't have a home :( */
/* ========================== *
* Hash utils
* ========================== */
/* FNV-1a parameters for different hash sizes:
* https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters
*/
#define HASH_FNV64_BASIS 0xCBF29CE484222325
INLINE u64 hash_fnv64(u64 seed, String s)
{
u64 hash = seed;
for (u64 i = 0; i < s.len; ++i) {
hash ^= (u8)s.text[i];
hash *= 0x100000001B3;
}
return hash;
}
/* ========================== *
* Merge sort
* ========================== */
/* Compare functions should
* return a positive value if a should go before b
* return a negative value if a should go after b
* return 0 otherwise
*/
#define SORT_COMPARE_FUNC_DEF(name, arg_a, arg_b, arg_udata) i32 name(void *arg_a, void *arg_b, void *arg_udata)
typedef SORT_COMPARE_FUNC_DEF(sort_compare_func, a, b, udata);
INLINE void merge_sort_internal(u8 *left, u8 *right, u8 *items, u64 left_count, u64 right_count, u64 item_size, sort_compare_func *callback, void *udata)
{
/* Sort */
u64 i = 0;
u64 l = 0;
u64 r = 0;
while (l < left_count && r < right_count) {
u8 *dst = items + (i * item_size);
u8 *left_item = left + (l * item_size);
u8 *right_item = right + (r * item_size);
++i;
if (callback(left_item, right_item, udata) > 0) {
MEMCPY(dst, left_item, item_size);
++l;
} else {
MEMCPY(dst, right_item, item_size);
++r;
}
}
/* Copy remaining */
if (l != left_count) {
u64 remaining_count = left_count - l;
u64 remaining_bytes = remaining_count * item_size;
u8 *dst = items + (i * item_size);
u8 *src = left + (l * item_size);
MEMCPY(dst, src, remaining_bytes);
} else if (r != right_count) {
u64 remaining_count = right_count - r;
u64 remaining_bytes = remaining_count * item_size;
u8 *dst = items + (i * item_size);
u8 *src = right + (r * item_size);
MEMCPY(dst, src, remaining_bytes);
}
}
INLINE void merge_sort(void *items, u64 item_count, u64 item_size, sort_compare_func *callback, void *udata)
{
if (item_count > 1) {
TempArena scratch = scratch_begin_no_conflict();
u64 left_count = item_count / 2;
u64 right_count = item_count - left_count;
u64 left_size = left_count * item_size;
u64 right_size = right_count * item_size;
u8 *left = arena_push_array_no_zero(scratch.arena, u8, left_size);
u8 *right = arena_push_array_no_zero(scratch.arena, u8, right_size);
MEMCPY(left, items, left_size);
MEMCPY(right, (u8 *)items + left_size, right_size);
merge_sort(left, left_count, item_size, callback, udata);
merge_sort(right, right_count, item_size, callback, udata);
merge_sort_internal(left, right, (u8 *)items, left_count, right_count, item_size, callback, udata);
scratch_end(scratch);
}
}
/* ========================== *
* Dict
*
* Simple chaining hash -> 64 bit value table for generic use
* ========================== */
Struct(DictEntry) {
u64 hash;
u64 value;
DictEntry *prev_in_bin;
DictEntry *next_in_bin;
DictEntry *prev;
DictEntry *next;
};
Struct(DictBin) {
DictEntry *first;
DictEntry *last;
};
Struct(Dict) {
u64 bins_count;
DictBin *bins;
DictEntry *first_free;
DictEntry *first;
DictEntry *last;
};
INLINE Dict *dict_init(Arena *arena, u64 bins_count)
{
__prof;
Dict *dict = arena_push(arena, Dict);
dict->bins_count = max_u64(bins_count, 1); /* Ensure at least 1 bin */
dict->bins = arena_push_array(arena, DictBin, dict->bins_count);
return dict;
}
INLINE void dict_reset(Dict *dict)
{
MEMZERO(dict->bins, sizeof(*dict->bins) * dict->bins_count);
if (dict->first) {
dict->last->next = dict->first_free;
dict->first_free = dict->first;
}
}
INLINE DictEntry *dict_ensure_entry(Arena *arena, Dict *dict, u64 hash)
{
__prof;
DictBin *bin = &dict->bins[hash % dict->bins_count];
DictEntry *entry = bin->first;
while (entry) {
if (hash == entry->hash) {
/* Existing match found */
break;
}
entry = entry->next_in_bin;
}
/* No match found, create new entry */
if (!entry) {
if (dict->first_free) {
entry = dict->first_free;
dict->first_free = entry->next;
} else {
entry = arena_push_no_zero(arena, DictEntry);
}
MEMZERO_STRUCT(entry);
entry->hash = hash;
if (bin->last) {
bin->last->next_in_bin = entry;
entry->prev_in_bin = bin->last;
} else {
bin->first = entry;
}
bin->last = entry;
if (dict->last) {
dict->last->next = entry;
entry->prev = dict->last;
} else {
dict->first = entry;
}
dict->last = entry;
}
return entry;
}
INLINE void dict_set(Arena *arena, Dict *dict, u64 hash, u64 value)
{
__prof;
DictEntry *entry = dict_ensure_entry(arena, dict, hash);
entry->value = value;
}
INLINE DictEntry *dict_get_entry(Dict *dict, u64 hash)
{
__prof;
DictEntry *result = 0;
DictBin *bin = &dict->bins[hash % dict->bins_count];
for (DictEntry *entry = bin->first; entry; entry = entry->next_in_bin) {
if (hash == entry->hash) {
/* Match found */
result = entry;
break;
}
}
return result;
}
INLINE u64 dict_get(Dict *dict, u64 hash)
{
__prof;
DictEntry *entry = dict_get_entry(dict, hash);
return entry ? entry->value : 0;
}
INLINE void dict_remove_entry(Dict *dict, DictEntry *entry)
{
/* Remove from bin */
{
DictBin *bin = &dict->bins[entry->hash % dict->bins_count];
DictEntry *prev_in_bin = entry->prev_in_bin;
DictEntry *next_in_bin = entry->next_in_bin;
if (prev_in_bin) {
prev_in_bin->next_in_bin = next_in_bin;
} else {
bin->first = next_in_bin;
}
if (next_in_bin) {
next_in_bin->prev_in_bin = prev_in_bin;
} else {
bin->last = prev_in_bin;
}
}
/* Remove from list */
{
DictEntry *prev = entry->prev;
DictEntry *next = entry->next;
if (prev) {
prev->next = next;
} else {
dict->first = next;
}
if (next) {
next->prev = prev;
} else {
dict->last = prev;
}
}
/* Insert into free list */
{
entry->next = dict->first_free;
dict->first_free = entry;
}
}