power_play/src/util.h
2025-05-15 08:42:12 -05:00

243 lines
6.6 KiB
C

#ifndef UTIL_H
#define UTIL_H
#include "sys.h"
#include "string.h"
#include "memory.h"
#include "arena.h"
#include "atomic.h"
#include "math.h"
#include "scratch.h"
/* 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, struct 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 an int < 0 if a < b
* return an int = 0 if a == b
* return an int > 0 if a > b
*/
#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) {
struct temp_arena 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);
}
}
/* ========================== *
* Fixed Dict
*
* Simple fixed-bin-count string -> pointer chaining hash table for generic use
* ========================== */
struct fixed_dict_entry {
struct string key;
void *value;
u64 hash;
struct fixed_dict_entry *next;
};
struct fixed_dict_bin {
struct fixed_dict_entry *entry_head;
};
struct fixed_dict {
u64 bins_count;
struct fixed_dict_bin *bins;
};
INLINE struct fixed_dict fixed_dict_init(struct arena *arena, u64 bins_count)
{
__prof;
struct fixed_dict dict = ZI;
bins_count = max_u64(bins_count, 1); /* Ensure at least 1 bin */
dict.bins_count = bins_count;
dict.bins = arena_push_array(arena, struct fixed_dict_bin, bins_count);
return dict;
}
/* arena and key must share lifetime with dict (this function does not copy the key) */
INLINE void fixed_dict_set(struct arena *arena, struct fixed_dict *dict, struct string key, void *value)
{
__prof;
u64 hash = hash_fnv64(HASH_FNV64_BASIS, key);
u64 index = hash % dict->bins_count;
struct fixed_dict_bin *bin = &dict->bins[index];
struct fixed_dict_entry *entry = bin->entry_head;
while (entry) {
if (hash == entry->hash) {
/* Existing match found, replace its contents */
entry->key = key;
entry->value = value;
return;
}
entry = entry->next;
}
/* No match found, create new entry */
entry = arena_push_no_zero(arena, struct fixed_dict_entry);
entry->key = key;
entry->value = value;
entry->hash = hash;
entry->next = bin->entry_head;
bin->entry_head = entry;
}
INLINE void *fixed_dict_get(const struct fixed_dict *dict, struct string key)
{
__prof;
u64 hash = hash_fnv64(HASH_FNV64_BASIS, key);
u64 index = hash % dict->bins_count;
struct fixed_dict_bin *bin = &dict->bins[index];
for (struct fixed_dict_entry *entry = bin->entry_head; entry; entry = entry->next) {
if (hash == entry->hash) {
/* Match found */
return entry->value;
}
}
return NULL;
}
/* ========================== *
* Sync flag
* ========================== */
struct sync_flag {
struct sys_mutex mutex;
struct sys_condition_variable cv;
b32 flag;
};
INLINE struct sync_flag sync_flag_alloc(void)
{
struct sync_flag sf = ZI;
sf.mutex = sys_mutex_alloc();
sf.cv = sys_condition_variable_alloc();
return sf;
}
INLINE void sync_flag_release(struct sync_flag *sf)
{
sys_mutex_release(&sf->mutex);
sys_condition_variable_release(&sf->cv);
}
INLINE void sync_flag_set(struct sync_flag *sf)
{
__prof;
struct sys_lock lock = sys_mutex_lock_e(&sf->mutex);
sf->flag = 1;
sys_condition_variable_broadcast(&sf->cv);
sys_mutex_unlock(&lock);
}
INLINE void sync_flag_wait(struct sync_flag *sf)
{
__prof;
struct sys_lock lock = sys_mutex_lock_s(&sf->mutex);
while (sf->flag != 1) {
sys_condition_variable_wait(&sf->cv, &lock);
}
sys_mutex_unlock(&lock);
}
/* ========================== *
* Sleep frame
* ========================== */
INLINE void sleep_frame(i64 last_frame_time_ns, i64 target_dt_ns)
{
__prof;
if (last_frame_time_ns != 0 && target_dt_ns > 0) {
i64 now_ns = sys_time_ns();
i64 last_frame_dt_ns = now_ns - last_frame_time_ns;
i64 sleep_time_ns = target_dt_ns - last_frame_dt_ns;
if (sleep_time_ns > 0) {
sys_sleep_precise(SECONDS_FROM_NS(sleep_time_ns));
}
}
}
#endif