321 lines
8.5 KiB
C
321 lines
8.5 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 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) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* ========================== *
|
|
* Dict
|
|
*
|
|
* Simple chaining hash -> 64 bit value table for generic use
|
|
* ========================== */
|
|
|
|
struct dict_entry {
|
|
u64 hash;
|
|
u64 value;
|
|
struct dict_entry *prev_in_bin;
|
|
struct dict_entry *next_in_bin;
|
|
struct dict_entry *prev;
|
|
struct dict_entry *next;
|
|
};
|
|
|
|
struct dict_bin {
|
|
struct dict_entry *first;
|
|
struct dict_entry *last;
|
|
};
|
|
|
|
struct dict {
|
|
u64 bins_count;
|
|
struct dict_bin *bins;
|
|
struct dict_entry *first_free;
|
|
struct dict_entry *first;
|
|
struct dict_entry *last;
|
|
};
|
|
|
|
INLINE struct dict dict_init(struct arena *arena, u64 bins_count)
|
|
{
|
|
__prof;
|
|
struct dict dict = ZI;
|
|
dict.bins_count = max_u64(bins_count, 1); /* Ensure at least 1 bin */
|
|
dict.bins = arena_push_array(arena, struct dict_bin, dict.bins_count);
|
|
return dict;
|
|
}
|
|
|
|
INLINE void dict_reset(struct 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 struct dict_entry *dict_ensure_entry(struct arena *arena, struct dict *dict, u64 hash)
|
|
{
|
|
__prof;
|
|
struct dict_bin *bin = &dict->bins[hash % dict->bins_count];
|
|
|
|
struct dict_entry *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, struct dict_entry);
|
|
}
|
|
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(struct arena *arena, struct dict *dict, u64 hash, u64 value)
|
|
{
|
|
__prof;
|
|
struct dict_entry *entry = dict_ensure_entry(arena, dict, hash);
|
|
entry->value = value;
|
|
}
|
|
|
|
INLINE struct dict_entry *dict_get_entry(struct dict *dict, u64 hash)
|
|
{
|
|
__prof;
|
|
struct dict_entry *result = NULL;
|
|
struct dict_bin *bin = &dict->bins[hash % dict->bins_count];
|
|
for (struct dict_entry *entry = bin->first; entry; entry = entry->next_in_bin) {
|
|
if (hash == entry->hash) {
|
|
/* Match found */
|
|
result = entry;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
INLINE u64 dict_get(struct dict *dict, u64 hash)
|
|
{
|
|
__prof;
|
|
struct dict_entry *entry = dict_get_entry(dict, hash);
|
|
return entry ? entry->value : 0;
|
|
}
|
|
|
|
INLINE void dict_remove_entry(struct dict *dict, struct dict_entry *entry)
|
|
{
|
|
/* Remove from bin */
|
|
{
|
|
struct dict_bin *bin = &dict->bins[entry->hash % dict->bins_count];
|
|
struct dict_entry *prev_in_bin = entry->prev_in_bin;
|
|
struct dict_entry *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 */
|
|
{
|
|
struct dict_entry *prev = entry->prev;
|
|
struct dict_entry *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;
|
|
}
|
|
}
|
|
|
|
/* ========================== *
|
|
* 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
|