#ifndef UTIL_H #define UTIL_H #include "sys.h" #include "string.h" #include "memory.h" #include "arena.h" #include "atomic.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 buffer buff) { u64 hash = seed; for (u64 i = 0; i < buff.size; ++i) { hash ^= (u8)buff.data[i]; hash *= 0x100000001B3; } return hash; } #define HASH_FNV128_BASIS U128(0x6C62272E07BB0142, 0x62B821756295C58D) INLINE u128 hash_fnv128(u128 seed, struct buffer buff) { /* FIXME: Verify MSVC version of 128 is same */ u128 hash = seed; for (u64 i = 0; i < buff.size; ++i) { u8 c = (u8)buff.data[i]; hash = u128_xor_u8(hash, c); hash = u128_mul(hash, U128(0x1000000, 0x000000000000013B)); } return hash; } /* ========================== * * Fixed Dict * * Simple fixed bucket-count string->value chaining dict for generic use * ========================== */ struct fixed_dict_entry { struct string key; void *value; u64 hash; struct fixed_dict_entry *next; }; struct fixed_dict_bucket { struct fixed_dict_entry *entry_head; }; struct fixed_dict { u64 buckets_count; struct fixed_dict_bucket *buckets; }; INLINE struct fixed_dict fixed_dict_init(struct arena *arena, u64 buckets_count) { __prof; struct fixed_dict dict = { 0 }; buckets_count = max_u64(buckets_count, 1); /* Ensure at least 1 bucket */ dict.buckets_count = buckets_count; dict.buckets = arena_push_array_zero(arena, struct fixed_dict_bucket, buckets_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, BUFFER_FROM_STRING(key)); u64 index = hash % dict->buckets_count; struct fixed_dict_bucket *bucket = &dict->buckets[index]; struct fixed_dict_entry *entry = bucket->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(arena, struct fixed_dict_entry); entry->key = key; entry->value = value; entry->hash = hash; entry->next = bucket->entry_head; bucket->entry_head = entry; } INLINE void *fixed_dict_get(const struct fixed_dict *dict, struct string key) { __prof; u64 hash = hash_fnv64(HASH_FNV64_BASIS, BUFFER_FROM_STRING(key)); u64 index = hash % dict->buckets_count; struct fixed_dict_bucket *bucket = &dict->buckets[index]; for (struct fixed_dict_entry *entry = bucket->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 = { 0 }; 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(sys_timestamp_t last_frame_time, f64 target_dt) { __prof; if (last_frame_time != 0 && target_dt > 0) { f64 last_frame_dt = sys_timestamp_seconds(sys_timestamp() - last_frame_time); f64 sleep_time = target_dt - last_frame_dt; if (sleep_time > 0) { sys_sleep_precise(sleep_time); } } } /* ========================== * * Collision testing * ========================== */ /* TODO: Remove this */ struct gjk_menkowski_point { struct v2 p0; /* Support point of first shape in dir */ struct v2 p1; /* Support point of second shape in -dir */ struct v2 p; /* Menkowski difference point */ }; struct gjk_extended_simplex { u32 len; struct gjk_menkowski_point a, b, c; }; struct gjk_extended_result { b32 colliding; struct v2 colliding_pen; struct gjk_extended_simplex final_simplex; }; struct v2 poly_support(struct v2_array a, struct v2 dir); struct v2 perp_towards_point(struct v2 start, struct v2 end, struct v2 p); struct v2_array menkowski(struct arena *arena, struct v2_array poly0, struct v2_array poly1); i32 poly_get_winding_order(struct v2_array poly); b32 gjk_boolean(struct v2_array poly0, struct v2_array poly1, u32 max_steps); struct gjk_extended_result gjk_extended(struct v2_array poly0, struct v2_array poly1, u32 max_steps); #endif