#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); } } /* ========================== * * Dict * * Simple chaining hash -> 64 bit value table for generic use * ========================== */ struct dict_entry { u64 hash; u64 value; struct dict_entry *next_in_bin; struct dict_entry *next; }; struct dict_bin { struct dict_entry *first; }; struct dict { u64 bins_count; struct dict_bin *bins; struct dict_entry *first; }; 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_set(struct arena *arena, struct dict *dict, u64 hash, u64 value) { __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) { entry = arena_push(arena, struct dict_entry); entry->value = value; entry->hash = hash; entry->next_in_bin = bin->first; entry->next = dict->first; dict->first = entry; bin->first = entry; } entry->value = value; } INLINE struct dict_entry *dict_get_entry(const 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(const struct dict *dict, u64 hash) { __prof; struct dict_entry *entry = dict_get_entry(dict, hash); return entry ? entry->value : 0; } /* ========================== * * 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