gb/gb.h

657 lines
17 KiB
C

/* gb.h - v0.01 - public domain C89/C99 helper library - no warranty implied; use at your own risk */
/* (Experimental) A C89/99 helper library geared towards game development */
/*
* LICENSE
* This software is in the public domain. Where that dedication is not
* recognized, you are granted a perpetual, irrevocable license to copy,
* distribute, and modify this file as you see fit.
*
* WARNING
* - This library is _highly_ experimental and features may not work as expected.
* - This also means that many functions are not documented.
*
*/
#ifndef GB_INCLUDE_GB_H
#define GB_INCLUDE_GB_H
#if defined(__cplusplus)
extern "C" {
#endif /* extern "C" */
/* NOTE(bill): Because static means three different things in C/C++
* Great Design(!)
*/
#ifndef global_variable
#define global_variable static
#define internal_linkage static
#define local_persist static
#endif
/* Example for static defines
global_variable const f32 TAU = 6.283185f;
global_variable void* g_memory;
internal_linkage void
some_function(...)
{
local_persist u32 count = 0;
...
count++;
...
}
*/
#if defined(_MSC_VER)
#define _ALLOW_KEYWORD_MACROS
#ifndef alignof /* Needed for MSVC 2013 'cause Microsoft "loves" standards */
#define alignof(x) __alignof(x)
#endif
#endif
/*
* System OS
*/
#if defined(_WIN32) || defined(_WIN64)
#ifndef GB_SYSTEM_WINDOWS
#define GB_SYSTEM_WINDOWS 1
#endif
#elif defined(__APPLE__) && defined(__MACH__)
#ifndef GB_SYSTEM_OSX
#define GB_SYSTEM_OSX 1
#endif
#elif defined(__unix__)
#ifndef GB_SYSTEM_UNIX
#define GB_SYSTEM_UNIX 1
#endif
#if defined(__linux__)
#ifndef GB_SYSTEM_LINUX
#define GB_SYSTEM_LINUX 1
#endif
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#ifndef GB_SYSTEM_FREEBSD
#define GB_SYSTEM_FREEBSD 1
#endif
#else
#error This UNIX operating system is not supported by gb.hpp
#endif
#else
#error This operating system is not supported by gb.hpp
#endif
#if defined(_MSC_VER)
/* Microsoft Visual Studio */
#define GB_COMPILER_MSVC 1
#elif defined(__clang__)
/* Clang */
#define GB_COMPILER_CLANG 1
#elif defined(__GNUC__) || defined(__GNUG__) && !(defined(__clang__) || defined(__INTEL_COMPILER))
/* GNU GCC/G++ Compiler */
#define GB_COMPILER_GNU_GCC 1
#elif defined(__INTEL_COMPILER)
/* Intel C++ Compiler */
#define GB_COMPILER_INTEL 1
#endif
/* Environment Bit Size */
#if defined(_WIN32) || defined(_WIN64)
#if defined(_WIN64)
#ifndef GB_ARCH_64_BIT
#define GB_ARCH_64_BIT 1
#endif
#else
#ifndef GB_ARCH_32_BIT
#define GB_ARCH_32_BIT 1
#endif
#endif
#endif
/* TODO(bill): Check if this works on clang */
#if defined(__GNUC__)
#if defined(__x86_64__) || defined(__ppc64__)
#ifndef GB_ARCH_64_BIT
#define GB_ARCH_64_BIT 1
#endif
#else
#ifndef GB_ARCH_32_BIT
#define GB_ARCH_32_BIT 1
#endif
#endif
#endif
#ifndef GB_EDIAN_ORDER
#define GB_EDIAN_ORDER
#define GB_IS_BIG_EDIAN (!*(unsigned char *)&(unsigned short){1})
#define GB_IS_LITTLE_EDIAN (!GB_IS_BIG_EDIAN)
#endif
#define GB_IS_POWER_OF_TWO(x) ((x) != 0) && !((x) & ((x) - 1))
#if !defined(GB_HAS_NO_CONSTEXPR)
#if defined(_GNUC_VER) && _GNUC_VER < 406 /* Less than gcc 4.06 */
#define GB_HAS_NO_CONSTEXPR 1
#elif defined(_MSC_VER) && _MSC_VER < 1900 /* Less than Visual Studio 2015/MSVC++ 14.0 */
#define GB_HAS_NO_CONSTEXPR 1
#elif !defined(__GXX_EXPERIMENTAL_CXX0X__) && __cplusplus < 201103L
#define GB_HAS_NO_CONSTEXPR 1
#endif
#endif
#if defined(GB_HAS_NO_CONSTEXPR)
#define GB_CONSTEXPR
#else
#define GB_CONSTEXPR constexpr
#endif
#ifndef GB_FORCE_INLINE
#if defined(_MSC_VER)
#define GB_FORCE_INLINE __forceinline
#else
#define GB_FORCE_INLINE __attribute__ ((__always_inline__))
#endif
#endif
#include <math.h>
#include <stdio.h>
#include <assert.h>
#if defined(GB_SYSTEM_WINDOWS)
#define NOMINMAX 1
#define VC_EXTRALEAN 1
#define WIN32_EXTRA_LEAN 1
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h> /* TODO(bill): Should we include only the needed headers? */
#include <mmsystem.h> /* Time functions */
#undef NOMINMAX
#undef VC_EXTRALEAN
#undef WIN32_EXTRA_LEAN
#undef WIN32_LEAN_AND_MEAN
#include <intrin.h>
#else
#include <pthread.h>
#include <sys/time.h>
#endif
#ifndef true
#define true (0==0)
#define false (0!=0)
#endif
#ifndef NULL
#define NULL ((void *)0)
#endif
#if !defined(GB_NO_DEBUG)
/* TODO(bill): Do a better assert */
#define GB_ASSERT(cond) assert(cond)
#else
#define GB_ASSERT(cond) ((void)(cond))
#endif
#define GB_STATIC_ASSERT(COND, MSG) typedef char gb__static_assertion_##MSG[(!!(COND))*2-1]
// token pasting madness:
#define GB_COMPILE_TIME_ASSERT3(cond, line) GB_STATIC_ASSERT(cond, static_assertion_at_line_##line)
#define GB_COMPILE_TIME_ASSERT2(cond, line) GB_COMPILE_TIME_ASSERT3(cond, line)
#define GB_COMPILE_TIME_ASSERT(cond) GB_COMPILE_TIME_ASSERT2(cond, __LINE__)
/* snprintf_msvc */
#if defined(_MSC_VER)
int gb__vsnprintf_compatible(char *buffer, size_t size, const char *format, va_list args)
{
int result = -1;
if (size > 0) result = _vsnprintf_s(buffer, size, _TRUNCATE, format, args);
if (result == -1) return _vscprintf(format, args);
return result;
}
int gb__snprintf_compatible(char *buffer, size_t size, const char *format, ...)
{
va_list args;
va_start(args, format);
int result = gb__vsnprintf_compatible(buffer, size, format, args);
va_end(args);
return result;
}
#if !defined(GB_DO_NOT_USE_MSVC_SPRINTF_FIX)
#define snprintf gb__snprintf_compatible
#define vsnprintf gb__vsnprintf_compatible
#endif /* GB_DO_NOT_USE_MSVC_SPRINTF_FIX */
#endif
#if defined(_MSC_VER)
typedef unsigned __int8 u8;
typedef signed __int8 s8;
typedef unsigned __int16 u16;
typedef signed __int16 s16;
typedef unsigned __int32 u32;
typedef signed __int32 s32;
typedef unsigned __int64 u64;
typedef signed __int64 s64;
#else
typedef uint8_t u8;
typedef int8_t s8;
typedef uint16_t u16;
typedef int16_t s16;
typedef uint32_t u32;
typedef int32_t s32;
typedef uint64_t u64;
typedef int64_t s64;
#endif
GB_COMPILE_TIME_ASSERT(sizeof(s8)==1);
GB_COMPILE_TIME_ASSERT(sizeof(s16)==2);
GB_COMPILE_TIME_ASSERT(sizeof(s32)==4);
GB_COMPILE_TIME_ASSERT(sizeof(s64)==8);
typedef float f32;
typedef double f64;
typedef s32 bool32;
#if defined(GB_ARCH_32_BIT)
typedef u32 usize;
typedef s32 ssize;
#elif defined(GB_ARCH_64_BIT)
typedef u64 usize;
typedef s64 ssize;
#else
#error Unknown architecture bit size
#endif
typedef uintptr_t uintptr;
typedef intptr_t intptr;
typedef ptrdiff_t ptrdiff;
#define U8_MIN 0u
#define U8_MAX 0xffu
#define S8_MIN (-0x7f - 1)
#define S8_MAX 0x7f
#define U16_MIN 0u
#define U16_MAX 0xffffu
#define S16_MIN (-0x7fff - 1)
#define S16_MAX 0x7fff
#define U32_MIN 0u
#define U32_MAX 0xffffffffu
#define S32_MIN (-0x7fffffff - 1)
#define S32_MAX 0x7fffffff
#define U64_MIN 0ull
#define U64_MAX 0xffffffffffffffffull
#define S64_MIN (-0x7fffffffffffffffll - 1)
#define S64_MAX 0x7fffffffffffffffll
#if defined(GB_ARCH_64_BIT) && !defined(GB_USIZE_MIX)
#define USIZE_MIX U64_MIN
#define USIZE_MAX U64_MAX
#define SSIZE_MIX S64_MIN
#define SSIZE_MAX S64_MAX
#elif defined(GB_ARCH_32_BIT) && !defined(GB_USIZE_MIX)
#define USIZE_MIX U32_MIN
#define USIZE_MAX U32_MAX
#define SSIZE_MIX S32_MIN
#define SSIZE_MAX S32_MAX
#endif
/* Casts */
/* NOTE(bill): Easier to grep/find for casts */
#define cast(Type, var) ((Type)(var))
/*
* Memory
*/
#define ARRAY_COUNT(x) ((sizeof(x)/sizeof(0[x])) / (cast(size_t, !(sizeof(x) % sizeof(0[x])))))
#define KILOBYTES(x) ( (x) * 1024ll)
#define MEGABYTES(x) (KILOBYTES(x) * 1024ll)
#define GIGABYTES(x) (MEGABYTES(x) * 1024ll)
#define TERABYTES(x) (GIGABYTES(x) * 1024ll)
typedef struct Mutex {
#if defined(GB_SYSTEM_WINDOWS)
HANDLE win32_mutex;
#else
pthread_mutex_t posix_mutex;
#endif
} Mutex;
Mutex mutex_make(void);
void mutex_destroy(Mutex *mutex);
void mutex_lock(Mutex *mutex);
bool32 mutex_try_lock(Mutex *mutex);
void mutex_unlock(Mutex *mutex);
typedef struct Atomic32 { u32 nonatomic; } Atomic32;
typedef struct Atomic64 { u64 nonatomic; } Atomic64;
u32 atomic32_load(const volatile Atomic32 *a);
void atomic32_store(volatile Atomic32 *a, u32 value);
u32 atomic32_compare_exchange_strong(volatile Atomic32 *a, u32 expected, u32 desired);
u32 atomic32_exchanged(volatile Atomic32 *a, u32 desired);
u32 atomic32_fetch_add(volatile Atomic32 *a, s32 desired);
u32 atomic32_fetch_and(volatile Atomic32 *a, u32 desired);
u32 atomic32_fetch_or(volatile Atomic32 *a, u32 desired);
u64 atomic64_load(const volatile Atomic64 *a);
void atomic64_store(volatile Atomic64 *a, u64 value);
u64 atomic64_compare_exchange_strong(volatile Atomic64 *a, u64 expected, u64 desired);
u64 atomic64_exchanged(volatile Atomic64 *a, u64 desired);
u64 atomic64_fetch_add(volatile Atomic64 *a, s64 desired);
u64 atomic64_fetch_and(volatile Atomic64 *a, u64 desired);
u64 atomic64_fetch_or(volatile Atomic64 *a, u64 desired);
typedef struct Semaphore {
#if defined(GB_SYSTEM_WINDOWS)
HANDLE win32_handle;
#else
Mutex mutex;
pthread_cond_t cond;
s32 count;
#endif
} Semaphore;
Semaphore semaphore_make(void);
void semaphore_destroy(Semaphore *s);
void semaphore_post(Semaphore *s);
void semaphore_post_count(Semaphore *s, u32 count);
void semaphore_wait(Semaphore *s);
typedef void(Thread_Procedure)(void*);
typedef struct Thread {
#if defined(GB_SYSTEM_WINDOWS)
HANDLE win32_handle;
#else
pthread_t posix_handle;
#endif
Thread_Procedure *functions;
void *data;
Semaphore semaphore;
usize stack_size;
bool32 is_running;
} Thread;
Thread thread_make(void);
void thread_destroy(Thread* t);
void thread_start(Thread* t, Thread_Procedure *func, void *data);
void thread_start_with_stack(Thread* t, Thread_Procedure *func, void *data, usize stack_size);
void thread_join(Thread* t);
bool32 thread_is_running(const Thread* t); /* NOTE(bill): Can this be just pass by value? */
u32 thread_current_id(void);
/* Default aligment for memory allocations */
#ifndef GB_DEFAULT_ALIGNMENT
#define GB_DEFAULT_ALIGNMENT 8
#endif
typedef struct Allocator {
/* Allocates the specified amount of memory aligned to the specified alignment */
void *(*alloc)(struct Allocator *a, usize size, usize align);
/* Deallocates an allocation made with alloc() */
void (*dealloc)(struct Allocator *a, void *ptr);
/* Returns the amount of usuable memory allocated at `ptr`.
* If the allocator does not support tracking of the allocation size,
* the function will return -1
*/
s64 (*allocated_size)(struct Allocator *a, const void *ptr);
/* Returns the total amount of memory allocated by this allocator */
/* If the allocator does not track memory, the function will return -1 */
s64 (*total_allocated)(struct Allocator *a);
} Allocator;
void *alloc_align(Allocator *a, usize size, usize align) { GB_ASSERT(a != NULL); return a->alloc(a, size, align); }
void *alloc(Allocator *a, usize size) { GB_ASSERT(a != NULL); return alloc_align(a, size, GB_DEFAULT_ALIGNMENT); }
#define alloc_struct(allocator, Type) cast(Type *, alloc_align(allocator, sizeof(Type), alignof(Type)))
#define alloc_array(allocator, Type, count) cast(Type *, alloc_align(allocator, sizeof(Type)*(count), alignof(Type)))
void dealloc(Allocator *a, void *ptr) { GB_ASSERT(a != NULL); if (ptr) a->dealloc(a, ptr); }
s64 allocated_size(Allocator *a, const void *ptr) { GB_ASSERT(a != NULL); return a->allocated_size(a, ptr); }
s64 total_allocated(Allocator *a) { GB_ASSERT(a != NULL); return a->total_allocated(a); }
typedef struct Heap {
Allocator base; /* NOTE(bill): Must be first into order to allow for polymorphism */
Mutex mutex;
bool32 use_mutex;
s64 total_allocated_count;
s64 allocation_count;
#if defined(GB_SYSTEM_WINDOWS)
HANDLE win32_heap_handle;
#endif
} Heap;
Heap heap_make(bool32 use_mutex);
void heap_destroy(Heap *heap);
typedef struct Arena {
Allocator base; /* NOTE(bill): Must be first into order to allow for polymorphism */
Allocator *backing;
void *physical_start;
s64 total_size;
s64 total_allocated_count;
s64 temp_count;
} Arena;
Arena arena_make_from_backing(Allocator *backing, usize size);
Arena arena_make_from_pointer(void *start, usize size);
void arena_destroy(Arena *arena);
void arena_clear(Arena *arena);
typedef struct Temporary_Arena_Memory {
Arena *arena;
s64 original_count;
} Temporary_Arena_Memory;
Temporary_Arena_Memory make_temporary_arena_memory(Arena *arena);
void temporary_arena_memory_free(Temporary_Arena_Memory t);
typedef struct Pool {
Allocator base; /* NOTE(bill): Must be first into order to allow for polymorphism */
Allocator *backing;
void *physical_start;
void *free_list;
usize block_size;
usize block_align;
s64 total_size;
} Pool;
Pool pool_make(Allocator *backing, usize num_blocks, usize block_size);
Pool pool_make_align(Allocator *backing, usize num_blocks, usize block_size, usize block_align);
void pool_destroy(Pool *pool);
/*
* Memory
*/
void *align_forward(void *ptr, usize align);
#define zero_struct(element) ((void)zero_size(&(element), sizeof(element)))
#define zero_array(ptr, Type, count) cast(Type, zero_size((ptr), sizeof(Type)*(count)))
void *zero_size(void *ptr, usize bytes);
/*
* Array
*/
/*
* Array structure:
*
*
* | Allocator * | usize count | usize capacity | char * |
* |
* `-- Returned pointer
*/
typedef struct Array_Header {
Allocator *allocator;
usize count;
usize capacity;
} Array_Header;
/* TODO(bill): Implement a c style array maybe like stb/stretchy_buffer.h but with a custom allocator */
#define array_header(arr) (cast(Array_Header *, arr) - 1)
#define array_make_count(allocator, Type, count) /* TODO(bill): */
#define array_make(allocator, Type) (array_make_count(allocator, Type, 0))
#define array_free(arr) (dealloc(array_header(arr)->allocator, array_header(arr)))
#define array_allocator(arr) (array_header(arr)->allocator)
#define array_count(arr) (array_header(arr)->count)
#define array_capacity(arr) (array_header(arr)->capacity)
#define array_append(arr, item) /* TODO(bill): */
#define array_append_array(arr, items, count) /* TODO(bill): */
#define array_pop(arr) (GB_ASSERT(array_header(arr)->count > 0), array_header(arr)->count--)
#define array_clear(arr) (array_header(arr)->count = 0)
#define array_resize(arr, count) /* TODO(bill): */
#define array_reserve(arr, capacity) /* TODO(bill): */
#define array_set_capacity(arr, capacity) /* TODO(bill): */
#define array_grow(arr, min_capacity) /* TODO(bill): */
/*
* String - c compatible strings
*/
typedef char * String;
typedef u32 String_Size;
typedef struct String_Header {
Allocator *allocator;
String_Size length;
String_Size capacity;
} String_Header;
#define GB_STRING_HEADER(str) (cast(String_Header *, str) - 1)
String string_make(Allocator *a, const char* str);
String string_make_length(Allocator *a, const void* str, String_Size num_bytes);
void string_free(String str);
String string_duplicate(Allocator *a, const String str);
String_Size string_length(const String str);
String_Size string_capacity(const String str);
String_Size string_available_space(const String str);
void string_clear(String str);
String string_append_string(String *str, const String other);
String string_append_string_length(String *str, const void *other, String_Size num_bytes);
String string_append_cstring(String *str, const char *other);
String string_set(String str, const char *cstr);
String string_make_space_for(String str, String_Size add_len);
String_Size string_allocation_size(const String str);
bool32 strings_are_equal(const String lhs, const String rhs);
String string_trim(String str, const char *cut_set);
String string_trim_space(String str); /* Whitespace ` \t\r\n\v\f` */
/*
* Hash
*/
u32 hash_adler32(const void *ket, u32 num_bytes);
u32 hash_crc32(const void* key, u32 num_bytes);
u64 hash_crc64(const void* key, usize num_bytes);
u32 hash_fnv32(const void* key, usize num_bytes);
u64 hash_fnv64(const void* key, usize num_bytes);
u32 hash_fnv32a(const void* key, usize num_bytes);
u64 hash_fnv64a(const void* key, usize num_bytes);
u32 hash_murmur32(const void* key, u32 num_bytes, u32 seed);
u64 hash_murmur64(const void* key, usize num_bytes, u64 seed);
/*
* Time
*/
/* TODO(bill): How should I this */
typedef struct Time { s64 microseconds; } Time;
#define TIME_ZERO (cast(Time, {0}))
Time time_now(void);
void time_sleep(Time time);
Time time_seconds(f32 s);
Time time_milliseconsd(s32 ms);
Time time_microseconds(s64 us);
f32 time_as_seconds(Time t);
s32 time_as_milliseconds(Time t);
s64 time_as_microseconds(Time t);
#if defined(__cplusplus)
}
#endif /* extern "C" */
#if defined(GB_IMPLEMENTATION)
#endif /* GB_IMPLEMENTATION */
#endif