diff options
| author | 2016-04-23 23:07:15 +0100 | |
|---|---|---|
| committer | 2016-04-23 23:07:15 +0100 | |
| commit | 41f87ef70484f05bbe83984e95ac45c233a7515b (patch) | |
| tree | 012d0f545e3775b8688551b498c98e4bd7657b15 /gb.h | |
| parent | New Libraries (diff) | |
Updates!!!!!
Diffstat (limited to 'gb.h')
| -rw-r--r-- | gb.h | 2955 |
1 files changed, 2568 insertions, 387 deletions
@@ -1,39 +1,47 @@ -// gb.h - v0.01 - public domain C helper library - no warranty implied; use at your own risk -// (Experimental) A C helper library geared towards game development +/* gb.h - v0.02a - Ginger Bill's C Helper Library - public domain + - no warranty implied; use at your own risk + + This is a single header file with a bunch of useful stuff + to replace the C/C++ standard library + +=========================================================================== + YOU MUST + + #define GB_IMPLEMENTATION + + in EXACTLY _one_ C or C++ file that includes this header, BEFORE the + include like this: + + #define GB_IMPLEMENTATION + #include "gb.h" + + All other files should just #include "gb.h" without #define +=========================================================================== + +Conventions used: + gbTypesAreLikeThis (None core types) + gb_functions_and_variables_like_this + Prefer // Comments + Never use _t suffix for types (I think they are stupid...) + + +Version History: + 0.02a - Bug fixes + 0.02 - Change naming convention and gbArray(Type) + 0.01 - Initial Version -/* 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. + This software is dual-licensed to the public domain and under the following + license: you are granted a perpetual, irrevocable license to copy, modify, + publish, and distribute this file as you see fit. WARNING - - This library is _highly_ experimental and features may not work as expected. + - This library is _slightly_ experimental and features may not work as expected. - This also means that many functions are not documented. -CONTENTS - - Common Macros - - Assert - - Types - - Cast macro (easy grepping) - - Memory - - Custom Allocation - - gb_Allocator - - gb_Arena - - gb_Pool - - gb_String - -TODO - - Mutex - - Atomics - - Semaphore - - Thread - - OS Types and Functions (File/IO/OS/etc.) -*/ +CREDITS + Written by Ginger Bill -/* -Version History: - 0.01 - Initial Version */ @@ -44,161 +52,294 @@ Version History: extern "C" { #endif -#include <stdarg.h> -#include <stddef.h> -// NOTE(bill): Because static means three different things in C/C++ -// Great design(!) -#ifndef local_persist -#define global static -#define internal static -#define local_persist static +#ifndef GB_EXTERN + #if defined(__cplusplus) + #define GB_EXTERN extern "C" + #else + #define GB_EXTERN extern + #endif #endif -// NOTE(bill): If I put gb_inline, I _want_ it inlined -#ifndef gb_inline - #if defined(_MSC_VER) - #define gb_inline __forceinline +#ifndef GB_DLL_EXPORT +#define GB_DLL_EXPORT __declspec(dllexport) +#endif + +#ifndef GB_DLL_IMPORT +#define GB_DLL_IMPORT __declspec(dllimport) +#endif + + +// NOTE(bill): Redefine for DLL, etc. +#ifndef GB_DEF + #ifdef GB_STATIC + #define GB_DEF static #else - #define gb_inline __attribute__ ((__always_inline__)) + #define GB_DEF extern + #endif +#endif + +#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 -#if !defined(GB_NO_STDIO) -#include <stdio.h> +#ifndef GB_EDIAN_ORDER +#define GB_EDIAN_ORDER + #define GB_IS_BIG_EDIAN (!*(u8*)&(u16){1}) + #define GB_IS_LITTLE_EDIAN (!GB_IS_BIG_EDIAN) #endif -#ifndef GB_ASSERT -#include <assert.h> -#define GB_ASSERT(cond) assert(cond) + +#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 + #endif +#else + #error This operating system is not supported #endif -#define GB_STATIC_ASSERT(cond, msg) typedef char gb__static_assertion_##msg[(!!(cond))*2-1] -// NOTE(bill): 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__) +#ifndef GB_STATIC_ASSERT + #define GB_STATIC_ASSERT3(cond, msg) typedef char static_assertion_##msg[(!!(cond))*2-1] + // NOTE(bill): Token pasting madness!! + #define GB_STATIC_ASSERT2(cond, line) GB_STATIC_ASSERT3(cond, static_assertion_at_line_##line) + #define GB_STATIC_ASSERT1(cond, line) GB_STATIC_ASSERT2(cond, line) + #define GB_STATIC_ASSERT(cond) GB_STATIC_ASSERT1(cond, __LINE__) +#endif -#if !defined(GB_NO_STDIO) && defined(_MSC_VER) - // snprintf_msvc - gb_inline int - gb__vsnprintf_compatible(char* buffer, size_t size, char const *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; - } - gb_inline int - gb__snprintf_compatible(char* buffer, size_t size, char const *format, ...) - { - int result = -1; - va_list args; - va_start(args, format); - 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 /* !defined(GB_NO_STDIO) */ +//////////////////////////////////////////////////////////////// +// +// Headers +// +// +#if defined(_WIN32) && !defined(__MINGW32__) + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif +#endif +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> // TODO(bill): Remove and replace with OS Specific stuff +#include <string.h> // NOTE(bill): For memcpy, memmove, memcmp, etc. + +#if defined(GB_SYSTEM_WINDOWS) + #define NOMINMAX 1 + #define WIN32_LEAN_AND_MEAN 1 + #define VC_EXTRALEAN 1 + #include <windows.h> +#else +#error Add OS Specific headers +#endif +#if !defined(GB_NO_STDLIB) +#include <stdlib.h> +#endif + +#ifndef gb_malloc +#define gb_malloc(sz) malloc(sz) +#endif + +#ifndef gb_mfree +#define gb_mfree(ptr) free(ptr) +#endif + + +//////////////////////////////////////////////////////////////// +// +// Base Types +// +// #if defined(_MSC_VER) - typedef unsigned __int8 u8; - typedef signed __int8 s8; + typedef unsigned __int8 u8; + typedef signed __int8 i8; typedef unsigned __int16 u16; - typedef signed __int16 s16; + typedef signed __int16 i16; typedef unsigned __int32 u32; - typedef signed __int32 s32; + typedef signed __int32 i32; typedef unsigned __int64 u64; - typedef signed __int64 s64; + typedef signed __int64 i64; #else #include <stdint.h> - typedef uint8_t u8; - typedef int8_t s8; + typedef uint8_t u8; + typedef int8_t i8; typedef uint16_t u16; - typedef int16_t s16; + typedef int16_t i16; typedef uint32_t u32; - typedef int32_t s32; + typedef int32_t i32; typedef uint64_t u64; - typedef int64_t s64; + typedef int64_t i64; #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); +GB_STATIC_ASSERT(sizeof(u8) == sizeof(i8)); +GB_STATIC_ASSERT(sizeof(u16) == sizeof(i16)); +GB_STATIC_ASSERT(sizeof(u32) == sizeof(i32)); +GB_STATIC_ASSERT(sizeof(u64) == sizeof(i64)); -typedef size_t usize; +GB_STATIC_ASSERT(sizeof(u8) == 1); +GB_STATIC_ASSERT(sizeof(u16) == 2); +GB_STATIC_ASSERT(sizeof(u32) == 4); +GB_STATIC_ASSERT(sizeof(u64) == 8); + +typedef size_t usize; +typedef ptrdiff_t isize; + +GB_STATIC_ASSERT(sizeof(usize) == sizeof(isize)); typedef uintptr_t uintptr; +typedef intptr_t intptr; typedef float f32; typedef double f64; -#include <stdbool.h> // NOTE(bill): To get false/true - -// Boolean Types -typedef s8 b8; -typedef s32 b32; - - +GB_STATIC_ASSERT(sizeof(f32) == 4); +GB_STATIC_ASSERT(sizeof(f64) == 8); + +typedef u16 char16; +typedef u32 char32; + +// NOTE(bill): I think C99 and C++ `bool` is stupid for numerous reasons but there are too many +// to write in this small comment. +typedef i8 b8; +typedef i16 b16; +typedef i32 b32; + +// NOTE(bill): Get true and false +#if !defined(__cplusplus) + #if defined(_MSC_VER) && _MSC_VER <= 1800 + #ifndef false + #define false 0 + #endif + #ifndef true + #define true 1 + #endif + #else + #include <stdbool.h> + #endif +#endif +#ifndef U8_MIN #define U8_MIN 0u #define U8_MAX 0xffu -#define S8_MIN (-0x7f - 1) -#define S8_MAX 0x7f +#define I8_MIN (-0x7f - 1) +#define I8_MAX 0x7f #define U16_MIN 0u #define U16_MAX 0xffffu -#define S16_MIN (-0x7fff - 1) -#define S16_MAX 0x7fff +#define I16_MIN (-0x7fff - 1) +#define I16_MAX 0x7fff #define U32_MIN 0u #define U32_MAX 0xffffffffu -#define S32_MIN (-0x7fffffff - 1) -#define S32_MAX 0x7fffffff +#define I32_MIN (-0x7fffffff - 1) +#define I32_MAX 0x7fffffff #define U64_MIN 0ull #define U64_MAX 0xffffffffffffffffull -#define S64_MIN (-0x7fffffffffffffffll - 1) -#define S64_MAX 0x7fffffffffffffffll +#define I64_MIN (-0x7fffffffffffffffll - 1) +#define I64_MAX 0x7fffffffffffffffll +#if defined(GB_ARCH_32_BIT) + #define USIZE_MIX U32_MIN + #define USIZE_MAX U32_MAX + #define ISIZE_MIX S32_MIN + #define ISIZE_MAX S32_MAX +#elif defined(GB_ARCH_64_BIT) + #define USIZE_MIX U64_MIN + #define USIZE_MAX U64_MAX + #define ISIZE_MIX I64_MIN + #define ISIZE_MAX I64_MAX +#else + #error Unknown architecture size +#endif +#define F32_MIN 1.17549435e-38f +#define F32_MAX 3.40282347e+38f + +#define F64_MIN 2.2250738585072014e-308 +#define F64_MAX 1.7976931348623157e+308 -#ifndef COUNT_OF -#define COUNT_OF(x) (sizeof((x)) / sizeof(0[(x)])) -#endif -// NOTE(bill): Allows for easy grep of casts -// NOTE(bill): Still not as type safe as C++ static_cast, reinterpret_cast, const_cast -#ifndef cast -#define cast(x) (x) #endif + + #ifndef NULL -#define NULL ((void *)0) + #if defined(__cplusplus) + #if __cplusplus >= 201103L + #define NULL nullptr + #else + #define NULL 0 + #endif + #else + #define NULL ((void *)0) + #endif #endif -#ifndef GB_UNUSED -#define GB_UNUSED(x) ((void)(sizeof(x))) + + +#if !defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER <= 1800 + #define inline __inline #endif -#ifndef gb_inline +#if !defined(gb_inline) + #if defined(_MSC_VER) + #define gb_restrict __restrict + #else + #define gb_restrict restrict + #endif +#endif + +// TODO(bill): Should force inline be a separate keyword and gb_inline be inline? +#if !defined(gb_inline) #if defined(_MSC_VER) #define gb_inline __forceinline #else @@ -206,39 +347,1169 @@ typedef s32 b32; #endif #endif +#if !defined(gb_no_inline) + #if defined(_MSC_VER) + #define gb_no_inline __declspec(noinline) + #else + #define gb_no_inline __attribute__ ((noinline)) + #endif +#endif + +// NOTE(bill): Easy to grep +// NOTE(bill): Not needed in macros +#ifndef cast +#define cast(Type) (Type) +#endif + + +// NOTE(bill): Because a signed sizeof is more useful +#ifndef gb_size_of +#define gb_size_of(x) (isize)(sizeof(x)) +#endif + +#ifndef gb_count_of +#define gb_count_of(x) ((gb_size_of(x)/gb_size_of(0[x])) / ((isize)(!(gb_size_of(x) % gb_size_of(0[x]))))) +#endif + +#ifndef gb_offset_of +#define gb_offset_of(Type, element) ((isize)&(((Type *)0)->element)) +#endif +#if defined(__cplusplus) +#ifndef gb_align_of + #if __cplusplus >= 201103L + #define gb_align_of(Type) (isize)alignof(Type) + #else +extern "C++" { + // NOTE(bill): Fucking Templates! + template <typename T> struct gbAlignment_Trick { char c; T member; }; + #define gb_align_of(Type) gb_offset_of(gbAlignment_Trick<Type>, member) +} + #endif +#endif +#else + #ifndef gb_align_of + #define gb_align_of(Type) gb_offset_of(struct { char c; Type member; }, member) + #endif +#endif +// NOTE(bill): I do which I had a type_of that was portable +#ifndef gb_swap +#define gb_swap(Type, a, b) do { Type tmp = (a); (a) = (b); (b) = tmp; } while (0) +#endif +// NOTE(bill): Because static means 3/4 different things in C/C++. Great design (!) +#ifndef gb_global +#define gb_global static // Global variables +#define gb_internal static // Internal linkage +#define gb_local_persist static // Local Persisting variables +#endif +#ifndef gb_unused +#define gb_unused(x) ((void)(gb_size_of(x))) +#endif + + + + + + +//////////////////////////////////////////////////////////////// +// +// Defer statement +// Akin to D's SCOPE_EXIT or +// similar to Go's defer but scope-based +// +// NOTE: C++11 (and above) only! +// +#if defined(__cplusplus) +extern "C++" { +namespace gb { + + // NOTE(bill): Stupid fucking templates + template <typename T> struct RemoveReference { typedef T Type; }; + template <typename T> struct RemoveReference<T &> { typedef T Type; }; + template <typename T> struct RemoveReference<T &&> { typedef T Type; }; + + // NOTE(bill): "Move" semantics - invented because the C++ committee are idiots (as a collective not as indiviuals (well a least some aren't)) + template <typename T> inline T &&forward(typename RemoveReference<T>::Type &t) { return static_cast<T &&>(t); } + template <typename T> inline T &&forward(typename RemoveReference<T>::Type &&t) { return static_cast<T &&>(t); } + template <typename T> inline T &&move(T &&t) { return static<typename RemoveReference<T>::Type &&>(t); } + template <typename F> + struct privDefer { + F f; + privDefer(F &&f) : f(forward<F>(f)) {} + ~privDefer() { f(); } + }; + template <typename F> privDefer<F> priv_defer_func(F &&f) { return privDefer<F>(forward<F>(f)); } + + #ifndef defer + #define GB_DEFER_1(x, y) x##y + #define GB_DEFER_2(x, y) GB_DEFER_1(x, y) + #define GB_DEFER_3(x) GB_DEFER_2(x, __COUNTER__) + #define defer(code) auto GB_DEFER_3(_defer_) = priv_defer_func([&](){code;}) + #endif +} // namespace gb +} + + // Example + #if 0 + gbMutex m; + gb_mutex_init(&m); + { + gb_mutex_lock(&m); + defer (gb_mutex_unlock(&m)); + + ... + } + #endif + +#endif + //////////////////////////////// -// // -// Memory // -// // +// +// Macro Fun! +// +// + +#ifndef GB_JOIN_MACROS +#define GB_JOIN_MACROS + #define GB_JOIN2_IND(a, b) a##b + #define GB_JOIN3_IND(a, b, c) a##b##c + + #define GB_JOIN2(a, b) GB_JOIN2_IND(a, b) + #define GB_JOIN3(a, b, c) GB_JOIN3_IND(a, b, c) +#endif + + +#ifndef GB_BIT +#define GB_BIT(x) (1<<x) +#endif + +#ifndef gb_min +#define gb_min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef gb_max +#define gb_max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef gb_clamp +#define gb_clamp(x, lower, upper) gb_min(gb_max((x), (lower)), (upper)) +#endif + +#ifndef gb_clamp01 +#define gb_clamp01(x) gb_clamp((x), 0, 1) +#endif + + +//////////////////////////////// +// +// Debug +// +// + + +#ifndef GB_DEBUG_TRAP + #if defined(_MSC_VER) + #define GB_DEBUG_TRAP() __debugbreak() + #else + #define GB_DEBUG_TRAP() __builtin_trap() + #endif +#endif + +// TODO(bill): This relies upon variadic macros which are not supported in MSVC 2003 and below, check for it if needed +#ifndef GB_ASSERT_MSG +#define GB_ASSERT_MSG(cond, msg, ...) do { \ + if (!(cond)) { \ + gb_assert_handler(#cond, __FILE__, cast(i64)__LINE__, msg, ##__VA_ARGS__); \ + GB_DEBUG_TRAP(); \ + } \ +} while (0) +#endif + +#ifndef GB_ASSERT +#define GB_ASSERT(cond) GB_ASSERT_MSG(cond, NULL) +#endif + +#ifndef GB_ASSERT_NOT_NULL +#define GB_ASSERT_NOT_NULL(ptr) GB_ASSERT_MSG((ptr) != NULL, #ptr " must not be NULL") +#endif + +// NOTE(bill): Things that shouldn't happen +#ifndef GB_PANIC +#define GB_PANIC(msg, ...) GB_ASSERT_MSG(0, msg, ##__VA_ARGS__) +#endif + +GB_DEF void gb_assert_handler(char const *condition, char const *file, i64 line, char const *msg, ...); + + + + + //////////////////////////////// +// +// Printing +// +// + +// Some compilers support applying printf-style warnings to user functions. +#if defined(__clang__) || defined(__GNUC__) +#define GB_PRINTF_ARGS(FMT) __attribute__((format(printf, FMT, (FMT+1)))) +#else +#define GB_PRINTF_ARGS(FMT) +#endif -#include <string.h> // For memcpy/memmove/memset/etc. +// TODO(bill): Should I completely rename these functions as they are a little weird to begin with? -#ifndef GB_IS_POWER_OF_TWO -#define GB_IS_POWER_OF_TWO(x) ((x) != 0) && !((x) & ((x)-1)) +GB_DEF i32 gb_printf(char const *fmt, ...) GB_PRINTF_ARGS(1); +GB_DEF i32 gb_printf_va(char const *fmt, va_list va); +GB_DEF i32 gb_fprintf(FILE *f, char const *fmt, ...) GB_PRINTF_ARGS(2); +GB_DEF i32 gb_fprintf_va(FILE *f, char const *fmt, va_list va); +GB_DEF char *gb_sprintf(char const *fmt, ...) GB_PRINTF_ARGS(1); // NOTE(bill): A locally persisting buffer is used internally +GB_DEF char *gb_sprintf_va(char const *fmt, va_list va); // NOTE(bill): A locally persisting buffer is used internally +GB_DEF i32 gb_snprintf(char *str, isize n, char const *fmt, ...) GB_PRINTF_ARGS(3); +GB_DEF i32 gb_snprintf_va(char *str, isize n, char const *fmt, va_list va); + +GB_DEF i32 gb_println(char const *str); +GB_DEF i32 gb_fprintln(FILE *f, char const *str); + + + +//////////////////////////////////////////////////////////////// +// +// Memory +// +// + +#ifndef gb_align_to +#define gb_align_to(value, alignment) (((value) + ((alignment)-1)) & ~((alignment) - 1)) +#endif + +#ifndef gb_is_power_of_two +#define gb_is_power_of_two(x) ((x) != 0) && !((x) & ((x)-1)) +#endif + +GB_DEF void *gb_align_forward(void *ptr, isize alignment); +GB_DEF void *gb_pointer_add(void *ptr, isize bytes); +GB_DEF void *gb_pointer_sub(void *ptr, isize bytes); + +GB_DEF void gb_zero_size(void *ptr, isize size); + +#ifndef gb_zero_struct +#define gb_zero_struct(t) gb_zero_size((t), gb_size_of(*(t))) // NOTE(bill): Pass pointer of struct +#define gb_zero_array(a, count) gb_zero_size((a), gb_size_of((a)[0])*count) +#endif + +GB_DEF void *gb_memcopy(void *dest, void const *source, isize size); +GB_DEF void *gb_memmove(void *dest, void const *source, isize size); +GB_DEF void *gb_memset(void *data, u8 byte_value, isize size); + +// NOTE(bill): Very similar to doing `*cast(T *)(&u)` +#ifndef GB_BIT_CAST +#define GB_BIT_CAST(dest, source) do { \ + GB_STATIC_ASSERT(gb_size_of(*(dest)) <= gb_size_of(source)); \ + gb_memcopy((dest), &(source), gb_size_of(*dest)); \ +} while (0) #endif -void *gb_align_forward(void *ptr, usize align); -void gb_zero_size(void *ptr, usize size); -#define gb_zero_struct(t) gb_zero_size((t), sizeof(*(t))) // NOTE(bill): Pass pointer of struct -#define gb_zero_array(a, count) gb_zero_size((a), sizeof((a)[0])*count) + +#ifndef gb_kilobytes +#define gb_kilobytes(x) ( (x) * (i64)(1024)) +#define gb_megabytes(x) (gb_kilobytes(x) * (i64)(1024)) +#define gb_gigabytes(x) (gb_megabytes(x) * (i64)(1024)) +#define gb_terabytes(x) (gb_gigabytes(x) * (i64)(1024)) +#endif + + +// Atomics +typedef struct gbAtomic32 { i32 volatile value; } gbAtomic32; +typedef struct gbAtomic64 { i64 volatile value; } gbAtomic64; + +GB_DEF i32 gb_atomic32_load(gbAtomic32 const volatile *a); +GB_DEF void gb_atomic32_store(gbAtomic32 volatile *a, i32 value); +GB_DEF i32 gb_atomic32_compare_exchange_strong(gbAtomic32 volatile *a, i32 expected, i32 desired); +GB_DEF i32 gb_atomic32_exchanged(gbAtomic32 volatile *a, i32 desired); +GB_DEF i32 gb_atomic32_fetch_add(gbAtomic32 volatile *a, i32 operand); +GB_DEF i32 gb_atomic32_fetch_and(gbAtomic32 volatile *a, i32 operand); +GB_DEF i32 gb_atomic32_fetch_or(gbAtomic32 volatile *a, i32 operand); + +GB_DEF i64 gb_atomic64_load(gbAtomic64 const volatile *a); +GB_DEF void gb_atomic64_store(gbAtomic64 volatile *a, i64 value); +GB_DEF i64 gb_atomic64_compare_exchange_strong(gbAtomic64 volatile *a, i64 expected, i64 desired); +GB_DEF i64 gb_atomic64_exchanged(gbAtomic64 volatile *a, i64 desired); +GB_DEF i64 gb_atomic64_fetch_add(gbAtomic64 volatile *a, i64 operand); +GB_DEF i64 gb_atomic64_fetch_and(gbAtomic64 volatile *a, i64 operand); +GB_DEF i64 gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand); + + +typedef struct gbMutex { +#if defined(GB_SYSTEM_WINDOWS) + HANDLE win32_handle; +#else + pthread_mutex_t posix_handle; +#endif +} gbMutex; + +GB_DEF void gb_mutex_init(gbMutex *m); +GB_DEF void gb_mutex_destroy(gbMutex *m); +GB_DEF void gb_mutex_lock(gbMutex *m); +GB_DEF b32 gb_mutex_try_lock(gbMutex *m); +GB_DEF void gb_mutex_unlock(gbMutex *m); + +// NOTE(bill): If you wanted a Scoped Mutex in C++, why not use the defer() construct? +// No need for a silly wrapper class +#if 0 +gbMutex m = {0}; +gb_mutex_init(&m); +{ + gb_mutex_lock(&m); + defer (gb_mutex_unlock(&m)); + + // Do whatever +} +#endif + + + +typedef struct gbSemaphore { +#if defined(GB_SYSTEM_WINDOWS) + HANDLE win32_handle; +#else + gbMutex mutex; + pthread_cond_t cond; + i32 count; +#endif +} gbSemaphore; + +GB_DEF void gb_semaphore_init(gbSemaphore *s); +GB_DEF void gb_semaphore_destroy(gbSemaphore *s); +GB_DEF void gb_semaphore_post(gbSemaphore *s, i32 count); +GB_DEF void gb_semaphore_wait(gbSemaphore *s); + + + + +#define GB_THREAD_PROC(name) void name(void *data) +;typedef GB_THREAD_PROC(gbThreadProc); + +typedef struct gbThread { +#if defined(GB_SYSTEM_WINDOWS) + HANDLE win32_handle; +#else + pthread_t posix_handle; +#endif + + gbThreadProc *proc; + void *data; + + gbSemaphore semaphore; + isize stack_size; + b32 is_running; +} gbThread; + +GB_DEF void gb_thread_init(gbThread *t); +GB_DEF void gb_thread_destory(gbThread *t); +GB_DEF void gb_thread_start(gbThread *t, gbThreadProc *proc, void *data); +GB_DEF void gb_thread_start_with_stack(gbThread *t, gbThreadProc *proc, void *data, isize stack_size); +GB_DEF void gb_thread_join(gbThread *t); +GB_DEF b32 gb_thread_is_running(gbThread const *t); +GB_DEF u32 gb_thread_current_id(void); + +//////////////////////////////////////////////////////////////// +// +// Virtual Memory +// +// + +#if 0 +typedef enum gbVmAllocationFlag { + GB_VM_COMMIT = GB_BIT(1), + GB_VM_RESERVE = GB_BIT(2), + GB_VM_RESET = GB_BIT(3), + GB_VM_RESET_UNDO = GB_BIT(4), +} gbVmAllocationFlag; + +typedef enum gbVmProtectionFlag { + GB_VM_PAGE_NO_ACCESS = GB_BIT(1), + GB_VM_PAGE_GUARD = GB_BIT(2), + GB_VM_PAGE_NO_CACHE = GB_BIT(3), + GB_VM_PAGE_WRITE_COMBINE = GB_BIT(4), +} gbVmProtectionFlag; + +typedef enum gbVmFreeType { + GB_VM_RELEASE = GB_BIT(1), + GB_VM_DECOMMIT = GB_BIT(2), +} gbVmFreeType; + +typedef struct gbVirtualMemory { + void *memory; + isize size; + u32 allocation_flags; + u32 protection_flags; +} gbVirtualMemory; + +GB_DEF gbVirtualMemory gb_vm_alloc(void *base_address, isize size, u32 allocation_flags, u32 protection_flags); +GB_DEF void gb_vm_free(gbVirtualMemory *vm); +#endif + + +//////////////////////////////////////////////////////////////// +// +// Custom Allocation +// +// + +typedef enum gbAllocationType { + GB_ALLOCATION_ALLOC, + GB_ALLOCATION_FREE, + GB_ALLOCATION_FREE_ALL, + GB_ALLOCATION_RESIZE, +} gbAllocationType; + +// NOTE(bill): This is useful so you can define an allocator of the same type and parameters +#define GB_ALLOCATOR_PROC(name) \ +void *name(void *allocator_data, gbAllocationType type, \ + isize size, isize alignment, \ + void *old_memory, isize old_size, \ + u64 options) +;typedef GB_ALLOCATOR_PROC(gbAllocatorProc); + +typedef struct gbAllocator { + gbAllocatorProc *proc; + void *data; +} gbAllocator; + +#ifndef GB_DEFAULT_MEMORY_ALIGNMENT +#define GB_DEFAULT_MEMORY_ALIGNMENT 4 +#endif + +GB_DEF void *gb_alloc_align(gbAllocator a, isize size, isize alignment); +GB_DEF void *gb_alloc(gbAllocator a, isize size); +GB_DEF void gb_free(gbAllocator a, void *ptr); +GB_DEF void gb_free_all(gbAllocator a); +GB_DEF void *gb_resize(gbAllocator a, void *ptr, isize old_size, isize new_size); +GB_DEF void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment); + +GB_DEF void *gb_alloc_copy(gbAllocator a, void const *src, isize size); +GB_DEF void *gb_alloc_copy_align(gbAllocator a, void const *src, isize size, isize alignment); + +GB_DEF char *gb_alloc_cstring(gbAllocator a, char const *str); + + +// NOTE(bill): These are very useful and the type case has saved me from numerous bugs +#ifndef gb_alloc_struct +#define gb_alloc_struct(allocator, Type) (Type *)gb_alloc_align(allocator, gb_size_of(Type)) +#define gb_alloc_array(allocator, Type, count) (Type *)gb_alloc(allocator, gb_size_of(Type) * (count)) +#endif + + + + + +GB_DEF gbAllocator gb_heap_allocator(void); +GB_DEF GB_ALLOCATOR_PROC(gb_heap_allocator_proc); + + + + + +typedef struct gbArena { + gbAllocator backing; + void *physical_start; + isize total_size; + isize total_allocated; + u32 temp_count; +} gbArena; + +GB_DEF void gb_arena_init_from_memory(gbArena *arena, void *start, isize size); +GB_DEF void gb_arena_init_from_allocator(gbArena *arena, gbAllocator backing, isize size); +GB_DEF void gb_arena_init_subarena(gbArena *arena, gbArena *parent_arena, isize size); +GB_DEF void gb_arena_free(gbArena *arena); + +GB_DEF isize gb_arena_alignment_of(gbArena *arena, isize alignment); +GB_DEF isize gb_arena_size_remaining(gbArena *arena, isize alignment); +GB_DEF void gb_arena_check(gbArena *arena); + + +GB_DEF gbAllocator gb_arena_allocator(gbArena *arena); +GB_DEF GB_ALLOCATOR_PROC(gb_arena_allocator_proc); + + + +typedef struct gbTempArenaMemory { + gbArena *arena; + isize original_count; +} gbTempArenaMemory; + +GB_DEF gbTempArenaMemory gb_temp_arena_memory_begin(gbArena *arena); +GB_DEF void gb_temp_arena_memory_end(gbTempArenaMemory tmp_mem); + + + + + + + + + +typedef struct gbPool { + gbAllocator backing; + + void *physical_start; + void *free_list; + + isize block_size; + isize block_align; + isize total_size; +} gbPool; + +GB_DEF void gb_pool_init(gbPool *pool, gbAllocator backing, isize num_blocks, isize block_size); +GB_DEF void gb_pool_init_align(gbPool *pool, gbAllocator backing, isize num_blocks, isize block_size, isize block_align); +GB_DEF void gb_pool_free(gbPool *pool); + + +GB_DEF gbAllocator gb_pool_allocator(gbPool *pool); +GB_DEF GB_ALLOCATOR_PROC(gb_pool_allocator_proc); + + + + + + + +//////////////////////////////////////////////////////////////// +// +// Char Functions +// +// + +GB_DEF char gb_char_to_lower(char c); +GB_DEF char gb_char_to_upper(char c); +GB_DEF b32 gb_char_is_space(char c); +GB_DEF b32 gb_char_is_digit(char c); +GB_DEF b32 gb_char_is_hex_digit(char c); +GB_DEF b32 gb_char_is_alpha(char c); +GB_DEF b32 gb_char_is_alphanumeric(char c); + + +// + +GB_DEF void gb_to_lower(char *str); +GB_DEF void gb_to_upper(char *str); + +GB_DEF isize gb_strlen(char const *str); +GB_DEF i32 gb_strcmp(char const *s1, char const *s2); +GB_DEF char * gb_strncpy(char *dest, char const *source, isize len); +GB_DEF i32 gb_strncmp(char const *s1, char const *s2, isize len); + +GB_DEF char const *gb_char_first_occurence(char const *s1, char c); +GB_DEF char const *gb_char_last_occurence(char const *s1, char c); + +GB_DEF void gb_cstr_concat(char *dest, isize dest_len, + char const *src_a, isize src_a_len, + char const *src_b, isize src_b_len); + + +//////////////////////////////////////////////////////////////// +// +// Windows UTF-8 Handling +// +// +// Windows doesn't handle 8 bit filenames well ('cause Micro$hit) + +GB_DEF char16 *gb_utf8_to_utf16(char16 *buffer, char *str, isize len); +GB_DEF char * gb_utf16_to_utf8(char *buffer, char16 *str, isize len); + + +//////////////////////////////////////////////////////////////// +// +// gbString - C Read-Only-Compatible +// +// + + +// Pascal like strings in C +typedef char *gbString; + + +// This is stored at the beginning of the string +// NOTE(bill): If you only need a small string, just use a standard c string or change the size +typedef struct gbStringHeader { + gbAllocator allocator; + isize length; + isize capacity; +} gbStringHeader; + +#define GB_STRING_HEADER(str) (cast(gbStringHeader *)(str) - 1) + +GB_DEF gbString gb_string_make(gbAllocator a, char const *str); +GB_DEF gbString gb_string_make_length(gbAllocator a, void const *str, isize num_bytes); +GB_DEF void gb_string_free(gbString str); + +GB_DEF gbString gb_string_duplicate(gbAllocator a, gbString const str); + +GB_DEF isize gb_string_length(gbString const str); +GB_DEF isize gb_string_capacity(gbString const str); +GB_DEF isize gb_string_available_space(gbString const str); + +GB_DEF void gb_string_clear(gbString str); + +GB_DEF gbString gb_string_append_string(gbString str, gbString const other); +GB_DEF gbString gb_string_append_string_length(gbString str, void const *other, isize num_bytes); +GB_DEF gbString gb_string_append_cstring(gbString str, char const *other); + +GB_DEF gbString gb_string_set(gbString str, char const *cstr); + +GB_DEF gbString gb_string_make_space_for(gbString str, isize add_len); +GB_DEF isize gb_string_allocation_size(gbString const str); + +GB_DEF b32 gb_strings_are_equal(gbString const lhs, gbString const rhs); + +GB_DEF gbString gb_string_trim(gbString str, char const *cut_set); +GB_DEF gbString gb_string_trim_space(gbString str); /* Whitespace ` \t\r\n\v\f` */ + + + +////////////////////////////////////////////////////////////////// +// +// Fixed Capacity Buffer (POD Types) +// +// + +#ifndef GB_BUFFER_TYPE +#define GB_BUFFER_TYPE +#endif + +#define gbBuffer(Type) struct { isize count, capacity; Type *data; } + +typedef gbBuffer(u8) gbByteBuffer; + +#define gb_buffer_init(x, allocator, cap) do { \ + void **data = cast(void **)&((x)->data); \ + gb_zero_struct(x); \ + (x)->capacity = (cap); \ + *data = gb_alloc((allocator), (cap)*gb_size_of((x)->data[0])); \ +} while (0) + +#define gb_buffer_free(x, allocator) do { gb_free(allocator, (x)->data); } while (0) + +#define gb_buffer_append(x, item) do { (x)->data[(x)->count++] = item; } while (0) + +#define gb_buffer_appendv(x, items, item_count) do { \ + GB_ASSERT(gb_size_of((items)[0]) == gb_size_of((x)->data[0])); \ + GB_ASSERT((x)->count+item_count <= (x)->capacity); \ + gb_memcopy((x)->data[a->count], (items), gb_size_of((x)->data[0])*(item_count)); \ + (x)->count += (item_count); \ +} while (0) + +#define gb_buffer_pop(x) do { GB_ASSERT((x)->count > 0); (x)->count--; } while (0) +#define gb_buffer_clear(x) do { (x)->count = 0; } while (0) + + + +////////////////////////////////////////////////////////////////// +// +// Dynamic Array (POD Types) +// +// + +// NOTE(bill): I know this is a macro hell but C is an old (and shit) language with no proper arrays +// Also why the fuck not?! It fucking works! And it has custom allocation, which is already better than C++! + +// NOTE(bill): Typedef every array or you get anonymous structures everywhere! +// e.g. typedef gbArray(int) gb_Int_Array; +#ifndef GB_ARRAY_TYPE +#define GB_ARRAY_TYPE + +#define gbArray(Type) struct { gbAllocator allocator; isize count, capacity; Type *data; } + + +typedef gbArray(void) gbVoidArray; // NOTE(bill): Useful for generic stuff + +// Available Procedures for gbArray(Type) +// gb_array_init +// gb_array_free +// gb_array_set_capacity +// gb_array_grow +// gb_array_append +// gb_array_appendv +// gb_array_pop +// gb_array_clear +// gb_array_resize +// gb_array_reserve +// + +#define gb_array_init(x, allocator_) do { gb_zero_struct(x); (x)->allocator = allocator_; } while (0) + +#define gb_array_free(x) do { \ + if ((x)->allocator.proc) { \ + gbAllocator a = (x)->allocator; \ + gb_free(a, (x)->data); \ + gb_array_init((x), a); \ + } \ +} while (0) + +#define gb_array_set_capacity(array, capacity) gb__array_set_capacity((array), (capacity), gb_size_of((array)->data[0])) +// NOTE(bill): Do not use the thing below directly, use the macro +GB_DEF void gb__array_set_capacity(void *array, isize capacity, isize element_size); + +#ifndef GB_ARRAY_GROW_FORMULA +#define GB_ARRAY_GROW_FORMULA(x) (2*(x) + 8) +#endif + +// TODO(bill): Decide on a decent growing formula for gbArray +#define gb_array_grow(x, min_capacity) do { \ + isize capacity = GB_ARRAY_GROW_FORMULA((x)->capacity); \ + if (capacity < (min_capacity)) \ + capacity = (min_capacity); \ + gb_array_set_capacity(x, capacity); \ +} while (0) + + +#define gb_array_append(x, item) do { \ + if ((x)->capacity < (x)->count+1) \ + gb_array_grow(x, 0); \ + (x)->data[(x)->count++] = (item); \ +} while (0) + +#define gb_array_appendv(x, items, item_count) do { \ + GB_ASSERT(gb_size_of((items)[0]) == gb_size_of((x)->data[0])); \ + if ((x)->capacity < (x)->count+(item_count)) \ + gb_array_grow(x, (x)->count+(item_count)); \ + gb_memcopy((x)->data[(x)->count], (items), gb_size_of((x)->data[0])*(item_count)); \ + (x)->count += (item_count); \ +} while (0) + + + +#define gb_array_pop(x) do { GB_ASSERT((x)->count > 0); (x)->count--; } while (0) +#define gb_array_clear(x) do { (x)->count = 0; } while (0) + +#define gb_array_resize(x, count) do { \ + if ((x)->capacity < (count)) \ + gb_array_grow(x, count); \ + (x)->count = (count); \ +} while (0) + + +#define gb_array_reserve(x, new_capacity) do { \ + if ((x)->capacity < (new_capacity)) \ + gb_array_set_capacity(x, new_capacity); \ +} while (0) + + +#endif /* GB_ARRAY_TYPE */ + + + + + + + + +//////////////////////////////////////////////////////////////// +// +// +// Hash_Table (POD Types) +// +// + +#if 0 +// TODO(bill): Hash Table and make it decent!!! + +// NOTE(bill): All keys are u64 +#ifndef GB_HASH_TABLE_TYPE +#define GB_HASH_TABLE_TYPE + +typedef struct gbHashTableEntry { + u64 key; + isize value; + isize next; +} gbHashTableEntry; + +#define GB_HASH_TABLE_HEADER \ + gbArray(isize) hashes; \ + gbArray(gbHashTableEntry) entries \ + +typedef struct gbHashTableHeader { + GB_HASH_TABLE_HEADER; +} gbHashTableHeader; + +#define gbHashTable(Type) struct { \ + GB_HASH_TABLE_HEADER; \ + gbArray(Type) data; \ +} + +typedef gbHashTable(void) gbHashTableVoid; // Generic Type + +#define gb_hash_table_init(h, allocator) do { \ + gb_array_init((h)->hashes, allocator); \ + gb_array_init((h)->entries, allocator); \ + gb_array_init((h)->data, allocator); \ +} while (0) + +#define gb_hash_table_free(h) do { \ + gb_free(&(h)->hashes); \ + gb_free(&(h)->entries); \ + gb_free(&(h)->data); \ +} while (0) + +#define gb_hash_table_header(h) (cast(gbHashTableHeader *)(h)) + + +// TODO(bill): Should the use have to pass the Hash Table and their array? + + + + +/* TODO(bill): Hash_Table Procs + gb_hash_table_init(h, allocator) + gb_hash_table_free(h) + + gb_hash_table_has(h, key) // Return false/true + gb_hash_table_get(h, key) // Return entries index + gb_hash_table_set(h, key, value) + gb_hash_table_remove(h, key) + gb_hash_table_reserve(h, capacity) + gb_hash_table_clear(h) + +*/ + + + +#endif + +#endif /* GB_HASH_TABLE_TYPE */ + + + +//////////////////////////////////////////////////////////////// +// +// File Handling +// +// +typedef u64 gbFileTime; + +typedef enum gbFileType { + GB_FILE_TYPE_READ = 1, + GB_FILE_TYPE_WRITE = 2, +} gbFileType; + +typedef struct gbFile { + void *handle; // File to fread/fwrite + char *path; + i64 size; + b32 is_open; + gbFileType type; + gbFileTime last_write_time; +} gbFile; + +typedef struct gbFileContents { + void *data; + isize size; +} gbFileContents; + + +GB_DEF b32 gb_file_create(gbFile *file, char const *filepath, ...); // TODO(bill): Give file permissions +GB_DEF b32 gb_file_open(gbFile *file, char const *filepath, ...); +GB_DEF b32 gb_file_close(gbFile *file); +GB_DEF b32 gb_file_read_at(gbFile *file, void *buffer, isize size, i64 offset); +GB_DEF b32 gb_file_write_at(gbFile *file, void const *buffer, isize size, i64 offset); +GB_DEF i64 gb_file_size(gbFile *file); +GB_DEF b32 gb_file_has_changed(gbFile *file); + +GB_DEF gbFileTime gb_file_last_write_time(char const *filepath, ...); + + +GB_DEF b32 gb_file_copy(char const *existing_filename, char const *new_filename, b32 fail_if_exists); +GB_DEF b32 gb_file_move(char const *existing_filename, char const *new_filename); + + +GB_DEF gbFileContents gb_read_entire_file_contents(gbAllocator a, b32 zero_terminate, char const *filepath, ...); + + +#ifndef GB_PATH_SEPARATOR + #if defined(GB_SYSTEM_WINDOWS) + #define GB_PATH_SEPARATOR '\\' + #else + #define GB_PATH_SEPARATOR '/' + #endif +#endif + +GB_DEF b32 gb_path_is_absolute(char const *path); +GB_DEF b32 gb_path_is_relative(char const *path); +GB_DEF b32 gb_path_is_root(char const *path); +GB_DEF char const *gb_path_base_name(char const *path); +GB_DEF char const *gb_path_extension(char const *path); + + +GB_DEF void gb_exit(u32 code); + + +//////////////////////////////////////////////////////////////// +// +// DLL Handling +// +// + +typedef void *gbDllHandle; +typedef void (*gbDllProc)(void); + +GB_DEF gbDllHandle gb_dll_load(char const *filepath, ...); +GB_DEF void gb_dll_unload(gbDllHandle dll); +GB_DEF gbDllProc gb_dll_proc_address(gbDllHandle dll, char const *proc_name); + + +//////////////////////////////////////////////////////////////// +// +// Time +// +// + +typedef struct gbDate { + i32 year; + i32 month; // 1 - Janurary, ... 12 - December + i32 day; // 1 - 31 + i32 day_of_week; // 0 - Sunday, ... 6 - Saturday + i32 hour; // 0 - 23 + i32 minute; // 0 - 59 + i32 second; // 0 - 60 (leap seconds) + i32 milliseconds; // 0 - 999 +} gbDate; + + +GB_DEF u64 gb_rdtsc(void); +GB_DEF f64 gb_time_now(void); + +GB_DEF void gb_get_system_date(gbDate *date); +GB_DEF void gb_get_local_date(gbDate *date); + + + +//////////////////////////////////////////////////////////////// +// +// Colour Type +// It's quite useful +// + +typedef union gbColour { + u32 rgba; // NOTE(bill): 0xaabbggrr + struct { u8 r, g, b, a; }; + u8 e[4]; +} gbColour; +GB_STATIC_ASSERT(gb_size_of(gbColour) == gb_size_of(u32)); + +gb_inline gbColour +gb_colour(f32 r, f32 g, f32 b, f32 a) +{ + gbColour result; + result.r = cast(u8)(gb_clamp01(r) * 255.0f); + result.g = cast(u8)(gb_clamp01(g) * 255.0f); + result.b = cast(u8)(gb_clamp01(b) * 255.0f); + result.a = cast(u8)(gb_clamp01(a) * 255.0f); + return result; +} + +gb_global gbColour const GB_COLOUR_WHITE = {0xffffffff}; +gb_global gbColour const GB_COLOUR_GREY = {0xff808080}; +gb_global gbColour const GB_COLOUR_BLACK = {0xff000000}; + +gb_global gbColour const GB_COLOUR_RED = {0xff0000ff}; +gb_global gbColour const GB_COLOUR_ORANGE = {0xff0099ff}; +gb_global gbColour const GB_COLOUR_YELLOW = {0xff00ffff}; +gb_global gbColour const GB_COLOUR_GREEN = {0xff00ff00}; +gb_global gbColour const GB_COLOUR_CYAN = {0xffffff00}; +gb_global gbColour const GB_COLOUR_BLUE = {0xffff0000}; +gb_global gbColour const GB_COLOUR_VIOLET = {0xffff007f}; +gb_global gbColour const GB_COLOUR_MAGENTA = {0xffff00ff}; + + + +#if defined(__cplusplus) +} +#endif + +#endif /* GB_INCLUDE_GB_H */ + + + + + + +//////////////////////////////////////////////////////////////// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// Implementation +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +//////////////////////////////////////////////////////////////// + +#if defined(GB_IMPLEMENTATION) && !defined(GB_IMPLEMENTATION_DONE) +#define GB_IMPLEMENTATION_DONE + +#if defined(__cplusplus) +extern "C" { +#endif + + +i32 +gb_printf(char const *fmt, ...) +{ + i32 res; + va_list va; + va_start(va, fmt); + res = gb_fprintf_va(stdout, fmt, va); + va_end(va); + return res; +} + + +i32 +gb_fprintf(FILE *f, char const *fmt, ...) +{ + i32 res; + va_list va; + va_start(va, fmt); + res = gb_fprintf_va(stdout, fmt, va); + va_end(va); + return res; +} + +char * +gb_sprintf(char const *fmt, ...) +{ + va_list va; + char *str; + va_start(va, fmt); + str = gb_sprintf_va(fmt, va); + va_end(va); + return str; +} + +i32 +gb_snprintf(char *str, isize n, char const *fmt, ...) +{ + i32 res; + va_list va; + va_start(va, fmt); + res = gb_snprintf_va(str, n, fmt, va); + va_end(va); + return res; +} + + +gb_inline i32 gb_printf_va(char const *fmt, va_list va) { return gb_fprintf_va(stdout, fmt, va); } +gb_inline i32 gb_fprintf_va(FILE *f, char const *fmt, va_list va) { return vfprintf(f, fmt, va); } + +gb_inline char * +gb_sprintf_va(char const *fmt, va_list va) +{ + gb_local_persist char buffer[1024]; + gb_snprintf_va(buffer, gb_size_of(buffer), fmt, va); + return buffer; +} + +gb_inline i32 +gb_snprintf_va(char *str, isize n, char const *fmt, va_list va) +{ + i32 res; +#if defined(_WIN32) + res = _vsnprintf(str, n, fmt, va); +#else + res = vsnprintf(str, n, fmt, va) +#endif + if (n) str[n-1] = 0; + // NOTE(bill): Unix returns length output would require, Windows returns negative when truncated. + return (res >= n || res < 0) ? -1 : res; +} + + +gb_inline i32 gb_println(char const *str) { return gb_fprintln(stdout, str); } + +gb_inline i32 +gb_fprintln(FILE *f, char const *str) +{ + i32 res; + res = gb_fprintf(f, str); + gb_fprintf(f, "\n"); + res++; + return res; +} + + + + + +void +gb_assert_handler(char const *condition, char const *file, i64 line, char const *msg, ...) +{ + gb_fprintf(stderr, "%s:%lld: Assert Failure: ", file, cast(long long)line); + if (condition) + gb_fprintf(stderr, "`%s` ", condition); + + if (msg) { + va_list va; + va_start(va, msg); + gb_fprintf(stderr, msg, va); + va_end(va); + } + + gb_fprintf(stderr, "\n"); +} + -#if defined(GB_IMPLEMENTATION) gb_inline void * -gb_align_forward(void *ptr, usize align) +gb_align_forward(void *ptr, isize align) { uintptr p; - usize modulo; + isize modulo; - GB_ASSERT(GB_IS_POWER_OF_TWO(align)); + GB_ASSERT(gb_is_power_of_two(align)); p = cast(uintptr)ptr; modulo = p % align; @@ -246,193 +1517,510 @@ gb_align_forward(void *ptr, usize align) return cast(void *)p; } -gb_inline void gb_zero_size(void *ptr, usize size) { memset(ptr, 0, size); } +gb_inline void *gb_pointer_add(void *ptr, isize bytes) { return cast(void *)(cast(u8 *)ptr + bytes); } +gb_inline void *gb_pointer_sub(void *ptr, isize bytes) { return cast(void *)(cast(u8 *)ptr - bytes); } -#endif // GB_IMPLEMENTATION +gb_inline void gb_zero_size(void *ptr, isize size) { gb_memset(ptr, 0, size); } +gb_inline void *gb_memcopy(void *dest, void const *source, isize size) { return memcpy(dest, source, size); } +gb_inline void *gb_memmove(void *dest, void const *source, isize size) { return memmove(dest, source, size); } +gb_inline void *gb_memset(void *data, u8 byte_value, isize size) { return memset(data, byte_value, size); } -//////////////////////////////// -// // -// Custom Allocation // -// // -//////////////////////////////// +gb_inline void *gb_alloc_align(gbAllocator a, isize size, isize alignment) { return a.proc(a.data, GB_ALLOCATION_ALLOC, size, alignment, NULL, 0, 0); } +gb_inline void *gb_alloc(gbAllocator a, isize size) { return gb_alloc_align(a, size, GB_DEFAULT_MEMORY_ALIGNMENT); } +gb_inline void gb_free(gbAllocator a, void *ptr) { a.proc(a.data, GB_ALLOCATION_FREE, 0, 0, ptr, 0, 0); } +gb_inline void gb_free_all(gbAllocator a) { a.proc(a.data, GB_ALLOCATION_FREE_ALL, 0, 0, NULL, 0, 0); } +gb_inline void *gb_resize(gbAllocator a, void *ptr, isize old_size, isize new_size) { return gb_resize_align(a, ptr, old_size, new_size, GB_DEFAULT_MEMORY_ALIGNMENT); } +gb_inline void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment) { return a.proc(a.data, GB_ALLOCATION_RESIZE, new_size, alignment, ptr, old_size, 0); }; + +gb_inline void *gb_alloc_copy(gbAllocator a, void const *src, isize size) { return gb_memcopy(gb_alloc(a, size), src, size); } +gb_inline void *gb_alloc_copy_align(gbAllocator a, void const *src, isize size, isize alignment) { return gb_memcopy(gb_alloc_align(a, size, alignment), src, size); } -typedef enum gb_Allocation_Type +gb_inline char * +gb_alloc_cstring(gbAllocator a, char const *str) { - GB_ALLOCATION_TYPE_ALLOC, - GB_ALLOCATION_TYPE_FREE, - GB_ALLOCATION_TYPE_FREE_ALL, - GB_ALLOCATION_TYPE_RESIZE, -} gb_Allocation_Type; + char *result; + isize len = gb_strlen(str); + result = cast(char *)gb_alloc_copy(a, str, len+1); + result[len] = '\0'; + return result; +} + + + + -#define GB_ALLOCATOR_PROCEDURE(name) void *name(void *allocator_data, gb_Allocation_Type type, usize size, usize alignment, void *old_memory, usize old_size, u32 options) -typedef GB_ALLOCATOR_PROCEDURE(gb_Allocator_Procedure); +//////////////////////////////////////////////////////////////// +// +// Concurrency +// -typedef struct gb_Allocator +#if defined(_MSC_VER) +gb_inline i32 +gb_atomic32_load(gbAtomic32 const volatile *a) { - gb_Allocator_Procedure *procedure; - void *data; -} gb_Allocator; + return a->value; +} +gb_inline void +gb_atomic32_store(gbAtomic32 volatile *a, i32 value) +{ + a->value = value; +} + +gb_inline i32 +gb_atomic32_compare_exchange_strong(gbAtomic32 volatile *a, i32 expected, i32 desired) +{ + return _InterlockedCompareExchange(cast(long volatile *)a, desired, expected); +} + +gb_inline i32 +gb_atomic32_exchanged(gbAtomic32 volatile *a, i32 desired) +{ + return _InterlockedExchange(cast(long volatile *)a, desired); +} + +gb_inline i32 +gb_atomic32_fetch_add(gbAtomic32 volatile *a, i32 operand) +{ + return _InterlockedExchangeAdd(cast(long volatile *)a, operand); +} + +gb_inline i32 +gb_atomic32_fetch_and(gbAtomic32 volatile *a, i32 operand) +{ + return _InterlockedAnd(cast(long volatile *)a, operand); +} + +gb_inline i32 +gb_atomic32_fetch_or(gbAtomic32 volatile *a, i32 operand) +{ + return _InterlockedOr(cast(long volatile *)a, operand); +} -#ifndef GB_DEFAULT_ALIGNMENT -#define GB_DEFAULT_ALIGNMENT 8 + +gb_inline i64 +gb_atomic64_load(gbAtomic64 const volatile *a) +{ +#if defined(GB_ARCH_64_BIT) + return a->value; +#else + // NOTE(bill): The most compatible way to get an atomic 64-bit load on x86 is with cmpxchg8b + i64 result; + __asm { + mov esi, a; + mov ebx, eax; + mov ecx, edx; + lock cmpxchg8b [esi]; + mov dword ptr result, eax; + mov dword ptr result[4], edx; + } + return result; #endif +} -gb_inline void *gb_alloc_align(gb_Allocator a, usize size, usize alignment) { return a.procedure(a.data, GB_ALLOCATION_TYPE_ALLOC, size, alignment, NULL, 0, 0); } -gb_inline void *gb_alloc(gb_Allocator a, usize size) { return gb_alloc_align(a, size, GB_DEFAULT_ALIGNMENT); } -gb_inline void gb_free(gb_Allocator a, void *ptr) { a.procedure(a.data, GB_ALLOCATION_TYPE_FREE, 0, 0, ptr, 0, 0); } -gb_inline void gb_free_all(gb_Allocator a) { a.procedure(a.data, GB_ALLOCATION_TYPE_FREE_ALL, 0, 0, NULL, 0, 0); } -gb_inline void *gb_resize(gb_Allocator a, void *ptr, usize new_size) { return a.procedure(a.data, GB_ALLOCATION_TYPE_RESIZE, new_size, 0, ptr, 0, 0); } +gb_inline void +gb_atomic64_store(gbAtomic64 volatile *a, i64 value) +{ +#if defined(GB_ARCH_64_BIT) + a->value = value; +#else + // NOTE(bill): The most compatible way to get an atomic 64-bit store on x86 is with cmpxchg8b + __asm { + mov esi, a; + mov ebx, dword ptr value; + mov ecx, dword ptr value[4]; + retry: + cmpxchg8b [esi]; + jne retry; + } +#endif +} -gb_inline void *gb_alloc_copy(gb_Allocator a, void* src, usize size) { return memcpy(gb_alloc(a, size), src, size); } -gb_inline void *gb_alloc_align_copy(gb_Allocator a, void* src, usize size, usize alignment) { return memcpy(gb_alloc_align(a, size, alignment), src, size); } +gb_inline i64 +gb_atomic64_compare_exchange_strong(gbAtomic64 volatile *a, i64 expected, i64 desired) +{ + return _InterlockedCompareExchange64(cast(i64 volatile *)a, desired, expected); +} -#define gb_alloc_struct(allocator, Type) (Type *)gb_alloc_align(allocator, sizeof(Type)) -#define gb_alloc_array(allocator, Type, count) (Type *)gb_alloc(allocator, sizeof(Type) * (count)) +gb_inline i64 +gb_atomic64_exchanged(gbAtomic64 volatile *a, i64 desired) +{ +#if defined(GB_ARCH_64_BIT) + return _InterlockedExchange64(cast(i64 volatile *)a, desired); +#else + i64 expected = a->value; + for (;;) { + i64 original = _InterlockedCompareExchange64(cast(i64 volatile *)a, desired, expected); + if (original == expected) + return original; + expected = original; + } +#endif +} +gb_inline i64 +gb_atomic64_fetch_add(gbAtomic64 volatile *a, i64 operand) +{ +#if defined(GB_ARCH_64_BIT) + return _InterlockedExchangeAdd64(cast(i64 volatile *)a, operand); +#else + i64 expected = a->value; + for (;;) { + i64 original = _InterlockedExchange64(cast(i64 volatile *)a, expected + operand, expected); + if (original == expected) + return original; + expected = original; + } +#endif +} + +gb_inline i64 +gb_atomic64_fetch_and(gbAtomic64 volatile *a, i64 operand) +{ +#if defined(GB_ARCH_64_BIT) + return _InterlockedAnd64(cast(i64 volatile *)a, operand); +#else + i64 expected = a->value; + for (;;) { + i64 original = _InterlockedCompareExchange64(cast(i64 volatile *)a, expected & operand, expected); + if (original == expected) + return original; + expected = original; + } +#endif +} + +gb_inline i64 +gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand) +{ +#if defined(GB_ARCH_64_BIT) + return _InterlockedAnd64(cast(i64 volatile *)a, operand); +#else + i64 expected = object->nonatomic; + for (;;) { + i64 original = _InterlockedCompareExchange64(cast(i64 volatile *)a, expected | operand, expected); + if (original == expected) + return original; + expected = original; + } +#endif +} + +#else +#error Implement atomics for this platform +#endif -typedef struct gb_Arena + + +gb_inline void gb_mutex_init(gbMutex *m) { m->win32_handle = CreateMutex(0, false, 0); } +gb_inline void gb_mutex_destroy(gbMutex *m) { CloseHandle(m->win32_handle); } +gb_inline void gb_mutex_lock(gbMutex *m) { WaitForSingleObject(m->win32_handle, INFINITE); } +gb_inline b32 gb_mutex_try_lock(gbMutex *m) { return WaitForSingleObject(m->win32_handle, 0) == WAIT_OBJECT_0; } +gb_inline void gb_mutex_unlock(gbMutex *m) { ReleaseMutex(m->win32_handle); } + + + + +gb_inline void +gb_semaphore_init(gbSemaphore *s) { - gb_Allocator backing; - void *physical_start; - usize total_size; - usize total_allocated_count; - usize prev_allocated_count; - u32 temp_count; -} gb_Arena; + s->win32_handle = CreateSemaphore(NULL, 0, I32_MAX, NULL); + GB_ASSERT_MSG(s->win32_handle != NULL, "CreateSemaphore: GetLastError = %d", GetLastError()); +} + +gb_inline void +gb_semaphore_destroy(gbSemaphore *s) +{ + BOOL err = CloseHandle(s->win32_handle); + GB_ASSERT_MSG(err != 0, "CloseHandle: GetLastError = %d", GetLastError()); +} -void gb_init_arena_from_memory(gb_Arena *arena, void *start, usize size); -void gb_init_arena_from_allocator(gb_Arena *arena, gb_Allocator backing, usize size); -void gb_free_arena(gb_Arena *arena); +gb_inline void +gb_semaphore_post(gbSemaphore *s, i32 count) +{ + BOOL err = ReleaseSemaphore(s->win32_handle, count, NULL); + GB_ASSERT_MSG(err != 0, "ReleaseSemaphore: GetLastError = %d", GetLastError()); +} -gb_Allocator gb_make_arena_allocator(gb_Arena *arena); -GB_ALLOCATOR_PROCEDURE(gb_arena_allocator_procedure); +gb_inline void +gb_semaphore_wait(gbSemaphore *s) +{ + DWORD result = WaitForSingleObject(s->win32_handle, INFINITE); + GB_ASSERT_MSG(result == WAIT_OBJECT_0, "WaitForSingleObject: GetLastError = %d", GetLastError()); +} +void +gb_thread_init(gbThread *t) +{ + gb_zero_struct(t); + t->win32_handle = INVALID_HANDLE_VALUE; + gb_semaphore_init(&t->semaphore); +} -typedef struct gb_Temp_Arena_Memory +void +gb_thread_destory(gbThread *t) { - gb_Arena *arena; - usize original_count; -} gb_Temp_Arena_Memory; + if (t->is_running) gb_thread_join(t); + gb_semaphore_destroy(&t->semaphore); +} -gb_Temp_Arena_Memory gb_begin_temp_arena_memory(gb_Arena *arena); -void gb_end_temp_arena_memory(gb_Temp_Arena_Memory tmp_mem); +gb_internal void +gb__thread_run(gbThread *t) +{ + gb_semaphore_post(&t->semaphore, 1); + t->proc(t->data); +} +#if defined(GB_SYSTEM_WINDOWS) + gb_internal DWORD WINAPI gb__thread_proc(void *arg) { gb__thread_run(cast(gbThread *)arg); return 0; } +#else + gb_internal void *gb__thread_proc(void *arg) { gb__thread_run(cast(gbThread *)arg); return NULL; } +#endif -typedef struct gb_Pool +gb_inline void gb_thread_start(gbThread *t, gbThreadProc *proc, void *data) { gb_thread_start_with_stack(t, proc, data, 0); } + +gb_inline void +gb_thread_start_with_stack(gbThread *t, gbThreadProc *proc, void *data, isize stack_size) { - gb_Allocator backing; + GB_ASSERT(!t->is_running); + GB_ASSERT(proc != NULL); + t->proc = proc; + t->data = data; + t->stack_size = stack_size; - void *physical_start; - void *free_list; + t->win32_handle = CreateThread(NULL, stack_size, gb__thread_proc, t, 0, NULL); + GB_ASSERT_MSG(t->win32_handle != NULL, "CreateThread: GetLastError = %d", GetLastError()); + + t->is_running = true; + gb_semaphore_wait(&t->semaphore); +} + +gb_inline void +gb_thread_join(gbThread *t) +{ + if (!t->is_running) return; + + WaitForSingleObject(t->win32_handle, INFINITE); + CloseHandle(t->win32_handle); + t->win32_handle = INVALID_HANDLE_VALUE; + t->is_running = false; +} + +gb_inline b32 gb_thread_is_running(gbThread const *t) { return t->is_running != 0; } + +gb_inline u32 +gb_thread_current_id(void) +{ + u32 thread_id; +#if defined(GB_SYSTEM_WINDOWS) + thread_id = GetCurrentThreadId(); +#elif defined(GB_SYSTEM_OSX) && defined(GB_ARCH_64_BIT) + asm("mov %%gs:0x00,%0" : "=r"(thread_id)); +#elif defined(GB_ARCH_32_BIT) + asm("mov %%gs:0x08,%0" : "=r"(thread_id)); +#elif defined(GB_ARCH_64_BIT) + asm("mov %%gs:0x10,%0" : "=r"(thread_id)); +#else + #error Unsupported architecture for thread::current_id() +#endif + + return thread_id; +} + + + + + + +gb_inline gbAllocator +gb_heap_allocator(void) +{ + gbAllocator a; + a.proc = gb_heap_allocator_proc; + a.data = NULL; + return a; +} + + +GB_ALLOCATOR_PROC(gb_heap_allocator_proc) +{ + switch (type) { + case GB_ALLOCATION_ALLOC: { + void *ptr; + isize actual_size = size + alignment; + ptr = gb_align_forward(gb_malloc(actual_size), alignment); + + return ptr; + } break; + + case GB_ALLOCATION_FREE: { + gb_mfree(old_memory); + } break; + + case GB_ALLOCATION_FREE_ALL: + break; - usize block_size; - usize block_align; - usize total_size; -} gb_Pool; + case GB_ALLOCATION_RESIZE: { + // TODO(bill): Check if ptr is on top of stack and just extend + gbAllocator a = gb_heap_allocator(); + if (!old_memory) return gb_alloc_align(a, size, alignment); + + if (size < old_size) + size = old_size; + + if (old_size == size) { + return old_memory; + } else { + void *new_memory = gb_alloc_align(a, size, alignment); + if (!new_memory) return NULL; + gb_memmove(new_memory, old_memory, gb_min(size, old_size)); + gb_free(a, old_memory); + return new_memory; + } + } break; + } + + return NULL; // NOTE(bill): Default return value +} -void gb_init_pool(gb_Pool *pool, gb_Allocator backing, usize num_blocks, usize block_size); -void gb_init_pool_align(gb_Pool *pool, gb_Allocator backing, usize num_blocks, usize block_size, usize block_align); -void gb_free_pool(gb_Pool *pool); -gb_Allocator gb_make_pool_allocator(gb_Pool *pool); -GB_ALLOCATOR_PROCEDURE(gb_pool_allocator_procedure); -#if defined(GB_IMPLEMENTATION) gb_inline void -gb_init_arena_from_memory(gb_Arena *arena, void *start, usize size) +gb_arena_init_from_memory(gbArena *arena, void *start, isize size) { - arena->backing.procedure = NULL; + arena->backing.proc = NULL; arena->backing.data = NULL; arena->physical_start = start; arena->total_size = size; - arena->total_allocated_count = 0; + arena->total_allocated = 0; arena->temp_count = 0; } gb_inline void -gb_init_arena_from_allocator(gb_Arena *arena, gb_Allocator backing, usize size) +gb_arena_init_from_allocator(gbArena *arena, gbAllocator backing, isize size) { arena->backing = backing; - arena->physical_start = gb_alloc(backing, size); + arena->physical_start = gb_alloc(backing, size); // NOTE(bill): Uses default alignment arena->total_size = size; - arena->total_allocated_count = 0; + arena->total_allocated = 0; arena->temp_count = 0; } +gb_inline void gb_arena_init_subarena(gbArena *arena, gbArena *parent_arena, isize size) { gb_arena_init_from_allocator(arena, gb_arena_allocator(parent_arena), size); } + + gb_inline void -gb_free_arena(gb_Arena *arena) +gb_arena_free(gbArena *arena) { - if (arena->backing.procedure) { + if (arena->backing.proc) { gb_free(arena->backing, arena->physical_start); arena->physical_start = NULL; } } +gb_inline isize +gb_arena_alignment_of(gbArena *arena, isize alignment) +{ + isize alignment_offset, result_pointer, mask; + GB_ASSERT(gb_is_power_of_two(alignment)); + + alignment_offset = 0; + result_pointer = cast(isize)arena->physical_start + arena->total_allocated; + mask = alignment - 1; + if (result_pointer & mask) + alignment_offset = alignment - (result_pointer & mask); + return alignment_offset; +} -gb_inline gb_Allocator -gb_make_arena_allocator(gb_Arena *arena) +gb_inline isize +gb_arena_size_remaining(gbArena *arena, isize alignment) { - gb_Allocator allocator; - allocator.procedure = gb_arena_allocator_procedure; + isize result = arena->total_size - (arena->total_allocated + gb_arena_alignment_of(arena, alignment)); + return result; +} + +gb_inline void gb_arena_check(gbArena *arena) { GB_ASSERT(arena->temp_count == 0); } + + + + + + +gb_inline gbAllocator +gb_arena_allocator(gbArena *arena) +{ + gbAllocator allocator; + allocator.proc = gb_arena_allocator_proc; allocator.data = arena; return allocator; } -GB_ALLOCATOR_PROCEDURE(gb_arena_allocator_procedure) +GB_ALLOCATOR_PROC(gb_arena_allocator_proc) { - gb_Arena *arena = cast(gb_Arena *)allocator_data; + gbArena *arena = cast(gbArena *)allocator_data; - GB_UNUSED(options); - GB_UNUSED(old_size); + gb_unused(options); + gb_unused(old_size); switch (type) { - case GB_ALLOCATION_TYPE_ALLOC: { + case GB_ALLOCATION_ALLOC: { void *ptr; - usize actual_size = size + alignment; + isize actual_size = size + alignment; // NOTE(bill): Out of memory - if (arena->total_allocated_count + actual_size > cast(usize)arena->total_size) + if (arena->total_allocated + actual_size > cast(isize)arena->total_size) return NULL; - ptr = gb_align_forward(cast(u8 *)arena->physical_start + arena->total_allocated_count, alignment); - arena->prev_allocated_count = arena->total_allocated_count; - arena->total_allocated_count += actual_size; + ptr = gb_align_forward(gb_pointer_add(arena->physical_start, arena->total_allocated), alignment); + arena->total_allocated += actual_size; return ptr; } break; - case GB_ALLOCATION_TYPE_FREE: { + case GB_ALLOCATION_FREE: // NOTE(bill): Free all at once // NOTE(bill): Use Temp_Arena_Memory if you want to free a block - } break; + // TODO(bill): Free it if it's on top of the stack + break; - case GB_ALLOCATION_TYPE_FREE_ALL: - arena->total_allocated_count = 0; + case GB_ALLOCATION_FREE_ALL: + arena->total_allocated = 0; break; - case GB_ALLOCATION_TYPE_RESIZE: { - // TODO(bill): Check if ptr is at the top - void *ptr = gb_alloc_align(gb_make_arena_allocator(arena), size, alignment); - memcpy(ptr, old_memory, size); - return ptr; + case GB_ALLOCATION_RESIZE: { + // TODO(bill): Check if ptr is on top of stack and just extend + gbAllocator a = gb_arena_allocator(arena); + if (!old_memory) return gb_alloc_align(a, size, alignment); + + if (size < old_size) + size = old_size; + + if (old_size == size) { + return old_memory; + } else { + void *new_memory = gb_alloc_align(a, size, alignment); + if (!new_memory) return NULL; + gb_memmove(new_memory, old_memory, gb_min(size, old_size)); + gb_free(a, old_memory); + return new_memory; + } } break; } @@ -440,90 +2028,94 @@ GB_ALLOCATOR_PROCEDURE(gb_arena_allocator_procedure) } -gb_inline gb_Temp_Arena_Memory -gb_begin_temp_arena_memory(gb_Arena *arena) +gb_inline gbTempArenaMemory +gb_temp_arena_memory_begin(gbArena *arena) { - gb_Temp_Arena_Memory tmp; + gbTempArenaMemory tmp; tmp.arena = arena; - tmp.original_count = arena->total_allocated_count; + tmp.original_count = arena->total_allocated; arena->temp_count++; return tmp; } gb_inline void -gb_end_temp_arena_memory(gb_Temp_Arena_Memory tmp) +gb_temp_arena_memory_end(gbTempArenaMemory tmp) { - GB_ASSERT(tmp.arena->total_allocated_count >= tmp.original_count); + GB_ASSERT(tmp.arena->total_allocated >= tmp.original_count); GB_ASSERT(tmp.arena->temp_count > 0); - tmp.arena->total_allocated_count = tmp.original_count; + tmp.arena->total_allocated = tmp.original_count; tmp.arena->temp_count--; } -void -gb_init_pool(gb_Pool *pool, gb_Allocator backing, usize num_blocks, usize block_size) +gb_inline void +gb_pool_init(gbPool *pool, gbAllocator backing, isize num_blocks, isize block_size) { - gb_init_pool_align(pool, backing, num_blocks, block_size, GB_DEFAULT_ALIGNMENT); + gb_pool_init_align(pool, backing, num_blocks, block_size, GB_DEFAULT_MEMORY_ALIGNMENT); } void -gb_init_pool_align(gb_Pool *pool, gb_Allocator backing, usize num_blocks, usize block_size, usize block_align) +gb_pool_init_align(gbPool *pool, gbAllocator backing, isize num_blocks, isize block_size, isize block_align) { - memset(pool, 0, sizeof(gb_Pool)); + isize actual_block_size, pool_size, block_index; + void *data, *curr; + uintptr *end; + + gb_zero_struct(pool); pool->backing = backing; pool->block_size = block_size; pool->block_align = block_align; - usize actual_block_size = block_size + block_align; - usize pool_size = num_blocks * actual_block_size; + actual_block_size = block_size + block_align; + pool_size = num_blocks * actual_block_size; - u8 *data = cast(u8 *)gb_alloc_align(backing, pool_size, block_align); + data = gb_alloc_align(backing, pool_size, block_align); - // Init intrusive freelist - u8 *curr = data; - for (usize block_index = 0; block_index < num_blocks-1; block_index++) { + // NOTE(bill): Init intrusive freelist + curr = data; + for (block_index = 0; block_index < num_blocks-1; block_index++) { uintptr *next = cast(uintptr *)curr; *next = cast(uintptr)curr + actual_block_size; - curr += actual_block_size; + curr = gb_pointer_add(curr, actual_block_size); } - uintptr *end = cast(uintptr*)curr; + end = cast(uintptr *)curr; *end = cast(uintptr)NULL; pool->physical_start = data; pool->free_list = data; } -void -gb_free_pool(gb_Pool *pool) +gb_inline void +gb_pool_free(gbPool *pool) { - if (pool->backing.procedure) { + if (pool->backing.proc) { gb_free(pool->backing, pool->physical_start); } } -gb_Allocator -gb_make_pool_allocator(gb_Pool *pool) +gb_inline gbAllocator +gb_pool_allocator(gbPool *pool) { - gb_Allocator allocator; - allocator.procedure = gb_pool_allocator_procedure; + gbAllocator allocator; + allocator.proc = gb_pool_allocator_proc; allocator.data = pool; return allocator; } -GB_ALLOCATOR_PROCEDURE(gb_pool_allocator_procedure) +GB_ALLOCATOR_PROC(gb_pool_allocator_proc) { - gb_Pool *pool = cast(gb_Pool *)allocator_data; + gbPool *pool = cast(gbPool *)allocator_data; - GB_UNUSED(options); - GB_UNUSED(old_size); + gb_unused(options); + gb_unused(old_size); switch (type) { - case GB_ALLOCATION_TYPE_ALLOC: { + case GB_ALLOCATION_ALLOC: { uintptr next_free; void *ptr; GB_ASSERT(size == pool->block_size); @@ -537,7 +2129,7 @@ GB_ALLOCATOR_PROCEDURE(gb_pool_allocator_procedure) return ptr; } break; - case GB_ALLOCATION_TYPE_FREE: { + case GB_ALLOCATION_FREE: { uintptr *next; if (old_memory == NULL) return NULL; @@ -547,13 +2139,13 @@ GB_ALLOCATOR_PROCEDURE(gb_pool_allocator_procedure) pool->total_size -= pool->block_size; } break; - case GB_ALLOCATION_TYPE_FREE_ALL: { + case GB_ALLOCATION_FREE_ALL: // TODO(bill): - } break; + break; - case GB_ALLOCATION_TYPE_RESIZE: { + case GB_ALLOCATION_RESIZE: // NOTE(bill): Cannot resize - } break; + break; } return NULL; @@ -565,89 +2157,212 @@ GB_ALLOCATOR_PROCEDURE(gb_pool_allocator_procedure) +gb_inline char +gb_char_to_lower(char c) +{ + if (c >= 'A' && c <= 'Z') + return 'a' + (c - 'A'); + return c; +} +gb_inline char +gb_char_to_upper(char c) +{ + if (c >= 'a' && c <= 'z') + return 'A' + (c - 'a'); + return c; +} +gb_inline b32 +gb_char_is_space(char c) +{ + if (c == ' ' || + c == '\t' || + c == '\n' || + c == '\r' || + c == '\f' || + c == '\v') + return true; + return false; +} +gb_inline b32 +gb_char_is_digit(char c) +{ + if (c >= '0' && c <= '9') + return true; + return false; +} -#endif // GB_IMPLEMENTATION +gb_inline b32 +gb_char_is_hex_digit(char c) +{ + if (gb_char_is_digit(c) || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F')) + return true; + return false; +} +gb_inline b32 +gb_char_is_alpha(char c) +{ + if ((c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z')) + return true; + return false; +} -//////////////////////////////// -// // -// gb_String - C Compatible // -// // -//////////////////////////////// +gb_inline b32 +gb_char_is_alphanumeric(char c) +{ + return gb_char_is_alpha(c) || gb_char_is_digit(c); +} -// Pascal like strings in C -typedef char *gb_String; -#ifndef GB_STRING_SIZE -#define GB_STRING_SIZE -typedef u32 gb_String_Size; -#endif +gb_inline void +gb_to_lower(char *str) +{ + if (!str) return; + while (*str) { + *str = gb_char_to_lower(*str); + str++; + } +} -// This is stored at the beginning of the string -// NOTE(bill): It is (2*sizeof(gb_String_Size) + 2*sizeof(void*)) (default: 16B (32bit), 24B (64bit)) -// NOTE(bill): If you only need a small string, just use a standard c string -typedef struct gb_String_Header +gb_inline void +gb_to_upper(char *str) { - gb_Allocator allocator; - gb_String_Size length; - gb_String_Size capacity; -} gb_String_Header; + if (!str) return; + while (*str) { + *str = gb_char_to_upper(*str); + str++; + } +} -#define GB_STRING_HEADER(str) (cast(gb_String_Header *)str - 1) -gb_String gb_string_make(gb_Allocator a, char const *str); -gb_String gb_string_make_length(gb_Allocator a, void const *str, gb_String_Size num_bytes); -void gb_string_free(gb_String str); +gb_inline isize +gb_strlen(char const *str) +{ + isize result = 0; + if (str) { + char const *end = str; + while (*end) end++; + result = end - str; + } + return result; +} -gb_String gb_string_duplicate(gb_Allocator a, gb_String const str); +gb_inline i32 +gb_strcmp(char const *s1, char const *s2) +{ + while (*s1 && (*s1 == *s2)) { + s1++, s2++; + } + return *(u8 *)s1 - *(u8 *)s2; +} -gb_String_Size gb_string_length(gb_String const str); -gb_String_Size gb_string_capacity(gb_String const str); -gb_String_Size gb_string_available_space(gb_String const str); -void gb_string_clear(gb_String str); +gb_inline char * +gb_strncpy(char *dest, char const *source, isize len) +{ + GB_ASSERT_NOT_NULL(dest); + if (source) { + char *str = dest; + while (len > 0 && *source) { + *str++ = *source++; + len--; + } + while (len > 0) { + *str++ = '\0'; + len--; + } + } + return dest; +} -gb_String gb_string_append_string(gb_String str, gb_String const other); -gb_String gb_string_append_string_length(gb_String str, void const *other, gb_String_Size num_bytes); -gb_String gb_string_append_cstring(gb_String str, char const *other); +gb_inline i32 +gb_strncmp(char const *s1, char const *s2, isize len) +{ + for(; len > 0; s1++, s2++, len--) { + if (*s1 != *s2) + return ((cast(uintptr)s1 < cast(uintptr)s2) ? -1 : +1); + else if (*s1 == '\0') + return 0; + } + return 0; +} -gb_String gb_string_set(gb_String str, char const *cstr); -gb_String gb_string_make_space_for(gb_String str, gb_String_Size add_len); -gb_String_Size gb_string_allocation_size(gb_String const str); +gb_inline char const * +gb_char_first_occurence(char const *s, char c) +{ + char ch = c; + for (; *s != ch; s++) { + if (*s == '\0') + return 0; + } + return s; +} + + +gb_inline char const * +gb_char_last_occurence(char const *s, char c) +{ + char const *result = NULL; + do { + if (*s == c) + result = s; + } while (*s++); + + return result; +} + + + +gb_inline void +gb_cstr_concat(char *dest, isize dest_len, + char const *src_a, isize src_a_len, + char const *src_b, isize src_b_len) +{ + GB_ASSERT(dest_len >= src_a_len+src_b_len+1); + if (dest) { + gb_memcopy(dest, src_a, src_a_len); + gb_memcopy(dest+src_a_len, src_b, src_b_len); + dest[src_a_len+src_b_len] = '\0'; + } +} + -b32 gb_strings_are_equal(gb_String const lhs, gb_String const rhs); -gb_String gb_string_trim(gb_String str, char const *cut_set); -gb_String gb_string_trim_space(gb_String str); /* Whitespace ` \t\r\n\v\f` */ -#if defined(GB_IMPLEMENTATION) -gb_inline void gb__string_set_length(gb_String str, gb_String_Size len) { GB_STRING_HEADER(str)->length = len; } -gb_inline void gb__string_set_capacity(gb_String str, gb_String_Size cap) { GB_STRING_HEADER(str)->capacity = cap; } -gb_inline gb_String -gb_string_make(gb_Allocator a, char const *str) + + +gb_inline void gb__string_set_length(gbString str, isize len) { GB_STRING_HEADER(str)->length = len; } +gb_inline void gb__string_set_capacity(gbString str, isize cap) { GB_STRING_HEADER(str)->capacity = cap; } + + +gb_inline gbString +gb_string_make(gbAllocator a, char const *str) { - gb_String_Size len = cast(gb_String_Size)(str ? strlen(str) : 0); + isize len = str ? gb_strlen(str) : 0; return gb_string_make_length(a, str, len); } -gb_String -gb_string_make_length(gb_Allocator a, void const *init_str, gb_String_Size num_bytes) +gbString +gb_string_make_length(gbAllocator a, void const *init_str, isize num_bytes) { - gb_String_Size header_size = sizeof(gb_String_Header); + isize header_size = gb_size_of(gbStringHeader); void *ptr = gb_alloc(a, header_size + num_bytes + 1); - gb_String str; - gb_String_Header *header; + gbString str; + gbStringHeader *header; if (!init_str) gb_zero_size(ptr, header_size + num_bytes + 1); if (ptr == NULL) return NULL; @@ -658,76 +2373,75 @@ gb_string_make_length(gb_Allocator a, void const *init_str, gb_String_Size num_b header->length = num_bytes; header->capacity = num_bytes; if (num_bytes && init_str) - memcpy(str, init_str, num_bytes); + gb_memcopy(str, init_str, num_bytes); str[num_bytes] = '\0'; return str; } gb_inline void -gb_string_free(gb_String str) +gb_string_free(gbString str) { if (str) { - gb_String_Header *header; - header = GB_STRING_HEADER(str); + gbStringHeader *header = GB_STRING_HEADER(str); gb_free(header->allocator, header); } } -gb_inline gb_String gb_string_duplicate(gb_Allocator a, gb_String const str) { return gb_string_make_length(a, str, gb_string_length(str)); } +gb_inline gbString gb_string_duplicate(gbAllocator a, gbString const str) { return gb_string_make_length(a, str, gb_string_length(str)); } -gb_inline gb_String_Size gb_string_length(gb_String const str) { return GB_STRING_HEADER(str)->length; } -gb_inline gb_String_Size gb_string_capacity(gb_String const str) { return GB_STRING_HEADER(str)->capacity; } +gb_inline isize gb_string_length(gbString const str) { return GB_STRING_HEADER(str)->length; } +gb_inline isize gb_string_capacity(gbString const str) { return GB_STRING_HEADER(str)->capacity; } -gb_inline gb_String_Size -gb_string_available_space(gb_String const str) +gb_inline isize +gb_string_available_space(gbString const str) { - gb_String_Header *h = GB_STRING_HEADER(str); + gbStringHeader *h = GB_STRING_HEADER(str); if (h->capacity > h->length) return h->capacity - h->length; return 0; } -gb_inline void gb_string_clear(gb_String str) { gb__string_set_length(str, 0); str[0] = '\0'; } +gb_inline void gb_string_clear(gbString str) { gb__string_set_length(str, 0); str[0] = '\0'; } -gb_inline gb_String gb_string_append_string(gb_String str, gb_String const other) { return gb_string_append_string_length(str, other, gb_string_length(other)); } +gb_inline gbString gb_string_append_string(gbString str, gbString const other) { return gb_string_append_string_length(str, other, gb_string_length(other)); } -gb_String -gb_string_append_string_length(gb_String str, void const *other, gb_String_Size other_len) +gbString +gb_string_append_string_length(gbString str, void const *other, isize other_len) { - gb_String_Size curr_len = gb_string_length(str); + isize curr_len = gb_string_length(str); str = gb_string_make_space_for(str, other_len); if (str == NULL) return NULL; - memcpy(str + curr_len, other, other_len); + gb_memcopy(str + curr_len, other, other_len); str[curr_len + other_len] = '\0'; gb__string_set_length(str, curr_len + other_len); return str; } -gb_inline gb_String -gb_string_append_cstring(gb_String str, char const *other) +gb_inline gbString +gb_string_append_cstring(gbString str, char const *other) { - return gb_string_append_string_length(str, other, cast(gb_String_Size)strlen(other)); + return gb_string_append_string_length(str, other, cast(isize)strlen(other)); } -gb_String -gb_string_set(gb_String str, char const *cstr) +gbString +gb_string_set(gbString str, char const *cstr) { - gb_String_Size len = cast(gb_String_Size)strlen(cstr); + isize len = gb_strlen(cstr); if (gb_string_capacity(str) < len) { str = gb_string_make_space_for(str, len - gb_string_length(str)); if (str == NULL) return NULL; } - memcpy(str, cstr, len); + gb_memcopy(str, cstr, len); str[len] = '\0'; gb__string_set_length(str, len); @@ -735,44 +2449,22 @@ gb_string_set(gb_String str, char const *cstr) } -local_persist void * -gb__string_realloc(gb_Allocator a, void *ptr, gb_String_Size old_size, gb_String_Size new_size) -{ - if (!ptr) return gb_alloc(a, new_size); - - if (new_size < old_size) - new_size = old_size; - - if (old_size == new_size) { - return ptr; - } else { - void *new_ptr = gb_alloc(a, new_size); - if (!new_ptr) - return NULL; - - memcpy(new_ptr, ptr, old_size); - gb_free(a, ptr); - return new_ptr; - } -} - - -gb_String -gb_string_make_space_for(gb_String str, gb_String_Size add_len) +gbString +gb_string_make_space_for(gbString str, isize add_len) { - gb_String_Size available = gb_string_available_space(str); + isize available = gb_string_available_space(str); // Return if there is enough space left if (available >= add_len) { return str; } else { - gb_String_Size new_len = gb_string_length(str) + add_len; + isize new_len = gb_string_length(str) + add_len; void *ptr = GB_STRING_HEADER(str); - gb_String_Size old_size = sizeof(struct gb_String_Header) + gb_string_length(str) + 1; - gb_String_Size new_size = sizeof(struct gb_String_Header) + new_len + 1; + isize old_size = gb_size_of(gbStringHeader) + gb_string_length(str) + 1; + isize new_size = gb_size_of(gbStringHeader) + new_len + 1; - void *new_ptr = gb__string_realloc(GB_STRING_HEADER(str)->allocator, ptr, old_size, new_size); + void *new_ptr = gb_resize(GB_STRING_HEADER(str)->allocator, ptr, old_size, new_size); if (new_ptr == NULL) return NULL; str = cast(char *)(GB_STRING_HEADER(new_ptr) + 1); @@ -782,18 +2474,18 @@ gb_string_make_space_for(gb_String str, gb_String_Size add_len) } } -gb_inline gb_String_Size -gb_string_allocation_size(gb_String const str) +gb_inline isize +gb_string_allocation_size(gbString const str) { - gb_String_Size cap = gb_string_capacity(str); - return sizeof(gb_String_Header) + cap; + isize cap = gb_string_capacity(str); + return gb_size_of(gbStringHeader) + cap; } gb_inline b32 -gb_strings_are_equal(gb_String const lhs, gb_String const rhs) +gb_strings_are_equal(gbString const lhs, gbString const rhs) { - gb_String_Size lhs_len, rhs_len, i; + isize lhs_len, rhs_len, i; lhs_len = gb_string_length(lhs); rhs_len = gb_string_length(rhs); if (lhs_len != rhs_len) @@ -808,24 +2500,24 @@ gb_strings_are_equal(gb_String const lhs, gb_String const rhs) } -gb_String -gb_string_trim(gb_String str, char const *cut_set) +gbString +gb_string_trim(gbString str, char const *cut_set) { char *start, *end, *start_pos, *end_pos; - gb_String_Size len; + isize len; start_pos = start = str; end_pos = end = str + gb_string_length(str) - 1; - while (start_pos <= end && strchr(cut_set, *start_pos)) + while (start_pos <= end && gb_char_first_occurence(cut_set, *start_pos)) start_pos++; - while (end_pos > start_pos && strchr(cut_set, *end_pos)) + while (end_pos > start_pos && gb_char_first_occurence(cut_set, *end_pos)) end_pos--; - len = cast(gb_String_Size)((start_pos > end_pos) ? 0 : ((end_pos - start_pos)+1)); + len = cast(isize)((start_pos > end_pos) ? 0 : ((end_pos - start_pos)+1)); if (str != start_pos) - memmove(str, start_pos, len); + gb_memmove(str, start_pos, len); str[len] = '\0'; gb__string_set_length(str, len); @@ -833,23 +2525,512 @@ gb_string_trim(gb_String str, char const *cut_set) return str; } -gb_inline gb_String gb_string_trim_space(gb_String str) { return gb_string_trim(str, " \t\r\n\v\f"); } +gb_inline gbString gb_string_trim_space(gbString str) { return gb_string_trim(str, " \t\r\n\v\f"); } + + + + +//////////////////////////////////////////////////////////////// +// +// Windows UTF-8 Handling +// +// + + +char16 * +gb_utf8_to_utf16(char16 *buffer, char *s, isize len) +{ + u8 *str = cast(u8 *)s; + char32 c; + isize i = 0; + len--; + while (*str) { + if (i >= len) + return NULL; + if (!(*str & 0x80)) { + buffer[i++] = *str++; + } else if ((*str & 0xe0) == 0xc0) { + if (*str < 0xc2) + return NULL; + c = (*str++ & 0x1f) << 6; + if ((*str & 0xc0) != 0x80) + return NULL; + buffer[i++] = c + (*str++ & 0x3f); + } else if ((*str & 0xf0) == 0xe0) { + if (*str == 0xe0 && + (str[1] < 0xa0 || str[1] > 0xbf)) + return NULL; + if (*str == 0xed && str[1] > 0x9f) // str[1] < 0x80 is checked below + return NULL; + c = (*str++ & 0x0f) << 12; + if ((*str & 0xc0) != 0x80) + return NULL; + c += (*str++ & 0x3f) << 6; + if ((*str & 0xc0) != 0x80) + return NULL; + buffer[i++] = c + (*str++ & 0x3f); + } else if ((*str & 0xf8) == 0xf0) { + if (*str > 0xf4) + return NULL; + if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) + return NULL; + if (*str == 0xf4 && str[1] > 0x8f) // str[1] < 0x80 is checked below + return NULL; + c = (*str++ & 0x07) << 18; + if ((*str & 0xc0) != 0x80) + return NULL; + c += (*str++ & 0x3f) << 12; + if ((*str & 0xc0) != 0x80) + return NULL; + c += (*str++ & 0x3f) << 6; + if ((*str & 0xc0) != 0x80) + return NULL; + c += (*str++ & 0x3f); + // UTF-8 encodings of values used in surrogate pairs are invalid + if ((c & 0xfffff800) == 0xd800) + return NULL; + if (c >= 0x10000) { + c -= 0x10000; + if (i+2 > len) + return NULL; + buffer[i++] = 0xd800 | (0x3ff & (c>>10)); + buffer[i++] = 0xdc00 | (0x3ff & (c )); + } + } else { + return NULL; + } + } + buffer[i] = 0; + return buffer; +} + +char * +gb_utf16_to_utf8(char *buffer, char16 *str, isize len) +{ + isize i = 0; + len--; + while (*str) { + if (*str < 0x80) { + if (i+1 > len) + return NULL; + buffer[i++] = (char) *str++; + } else if (*str < 0x800) { + if (i+2 > len) + return NULL; + buffer[i++] = 0xc0 + (*str >> 6); + buffer[i++] = 0x80 + (*str & 0x3f); + str += 1; + } else if (*str >= 0xd800 && *str < 0xdc00) { + char32 c; + if (i+4 > len) + return NULL; + c = ((str[0] - 0xd800) << 10) + ((str[1]) - 0xdc00) + 0x10000; + buffer[i++] = 0xf0 + (c >> 18); + buffer[i++] = 0x80 + ((c >> 12) & 0x3f); + buffer[i++] = 0x80 + ((c >> 6) & 0x3f); + buffer[i++] = 0x80 + ((c ) & 0x3f); + str += 2; + } else if (*str >= 0xdc00 && *str < 0xe000) { + return NULL; + } else { + if (i+3 > len) + return NULL; + buffer[i++] = 0xe0 + (*str >> 12); + buffer[i++] = 0x80 + ((*str >> 6) & 0x3f); + buffer[i++] = 0x80 + ((*str ) & 0x3f); + str += 1; + } + } + buffer[i] = 0; + return buffer; +} + + + + +gb_no_inline void +gb__array_set_capacity(void *array_, isize capacity, isize element_size) +{ + // NOTE(bill): I know this is unsafe so don't call this function directly + gbVoidArray *a = cast(gbVoidArray *)array_; + void *data = NULL; + + GB_ASSERT(element_size > 0); + + if (capacity == a->capacity) + return; + + if (capacity < a->count) { + if (a->capacity < capacity) { + isize new_capacity = GB_ARRAY_GROW_FORMULA(a->capacity); + if (new_capacity < capacity) + new_capacity = capacity; + gb__array_set_capacity(a, new_capacity, element_size); + } + a->count = capacity; + } + + if (capacity > 0) { + data = gb_alloc(a->allocator, element_size*capacity); + gb_memcopy(data, a->data, element_size*a->count); + } + gb_free(a->allocator, a->data); + a->data = data; + a->capacity = capacity; +} + + + + + + +//////////////////////////////////////////////////////////////// +// +// File Handling +// +// + +b32 +gb_file_create(gbFile *file, char const *filepath, ...) +{ + va_list va; + char *path; + gb_zero_struct(file); + + va_start(va, filepath); + path = gb_sprintf_va(filepath, va); + va_end(va); + + file->handle = fopen(path, "wb"); + if (file->handle) { + file->path = gb_alloc_cstring(gb_heap_allocator(), path); + file->size = gb_file_size(file); + file->is_open = true; + file->type = GB_FILE_TYPE_WRITE; + file->last_write_time = gb_file_last_write_time(file->path); + return true; + } + return false; +} + + + +b32 +gb_file_open(gbFile *file, char const *filepath, ...) +{ + va_list va; + char *path; + gb_zero_struct(file); + + va_start(va, filepath); + path = gb_sprintf_va(filepath, va); + va_end(va); + + file->handle = fopen(path, "rb"); + if (file->handle) { + file->path = gb_alloc_cstring(gb_heap_allocator(), path); + file->size = gb_file_size(file); + file->is_open = true; + file->type = GB_FILE_TYPE_READ; + file->last_write_time = gb_file_last_write_time(file->path); + return true; + } + return false; +} + +gb_inline b32 +gb_file_close(gbFile *file) +{ + b32 result = true; + if (file && file->handle) + result = fclose(cast(FILE *)file->handle) != 0; // TODO(bill): Handle fclose errors + + if (file->path) gb_free(gb_heap_allocator(), file->path); + file->is_open = false; + + return result; +} + +gb_inline b32 +gb_file_read_at(gbFile *file, void *buffer, isize size, i64 offset) +{ + i64 prev_cursor_pos; + + GB_ASSERT(file->type == GB_FILE_TYPE_READ); + + prev_cursor_pos = ftell(cast(FILE *)file->handle); + fseek(cast(FILE *)file->handle, offset, SEEK_SET); + fread(buffer, 1, size, cast(FILE *)file->handle); + fseek(cast(FILE *)file->handle, prev_cursor_pos, SEEK_SET); + return true; +} + +gb_inline b32 +gb_file_write_at(gbFile *file, void const *buffer, isize size, i64 offset) +{ + isize written_size; + i64 prev_cursor_pos; + + GB_ASSERT(file->type == GB_FILE_TYPE_WRITE); + + prev_cursor_pos = ftell(cast(FILE *)file->handle); + fseek(cast(FILE *)file->handle, offset, SEEK_SET); + + written_size = fwrite(buffer, 1, size, cast(FILE *)file->handle); + fseek(cast(FILE *)file->handle, prev_cursor_pos, SEEK_SET); + if (written_size != size) { + GB_PANIC("Failed to write file data"); + return false; + } + + return true; +} + +gb_inline i64 +gb_file_size(gbFile *file) +{ + i64 result_size; + + fseek(cast(FILE *)file->handle, 0, SEEK_END); + result_size = cast(i64)ftell(cast(FILE *)file->handle); + fseek(cast(FILE *)file->handle, 0, SEEK_SET); + return result_size; +} + +b32 +gb_file_has_changed(gbFile *file) +{ + b32 result = false; + gbFileTime last_write_time = gb_file_last_write_time(file->path); + if (file->last_write_time != last_write_time) { + result = true; + file->last_write_time = last_write_time; + } + return result; +} + + + +#if defined(GB_SYSTEM_WINDOWS) +gbFileTime +gb_file_last_write_time(char const *filepath, ...) +{ + ULARGE_INTEGER li = {0}; + FILETIME last_write_time = {0}; + WIN32_FILE_ATTRIBUTE_DATA data = {0}; + + va_list va; + va_start(va, filepath); + if (GetFileAttributesEx(gb_sprintf_va(filepath, va), GetFileExInfoStandard, &data)) + last_write_time = data.ftLastWriteTime; + va_end(va); + + li.LowPart = last_write_time.dwLowDateTime; + li.HighPart = last_write_time.dwHighDateTime; + return cast(gbFileTime)li.QuadPart; +} + +gb_inline b32 +gb_file_copy(char const *existing_filename, char const *new_filename, b32 fail_if_exists) +{ + return CopyFile(existing_filename, new_filename, fail_if_exists); +} + +gb_inline b32 +gb_file_move(char const *existing_filename, char const *new_filename) +{ + return MoveFile(existing_filename, new_filename); +} + + + +#else +#error #endif -//////////////////////////////// -// // -// Unfinished code // -// // -//////////////////////////////// +gbFileContents +gb_read_entire_file_contents(gbAllocator a, b32 zero_terminate, char const *filepath, ...) +{ + gbFileContents result = {0}; + gbFile file = {0}; + char *path; + va_list va; + va_start(va, filepath); + path = gb_sprintf_va(filepath, va); + va_end(va); + + if (gb_file_open(&file, path)) { + i64 file_size = gb_file_size(&file); + if (file_size > 0) { + result.data = gb_alloc(a, zero_terminate ? file_size+1 : file_size); + result.size = file_size; + gb_file_read_at(&file, result.data, result.size, 0); + if (zero_terminate) { + u8 *str = cast(u8 *)result.data; + str[file_size] = '\0'; + } + } + gb_file_close(&file); + } -#if defined(__cplusplus) + return result; +} + + + + + +gb_inline b32 +gb_path_is_absolute(char const *path) +{ + b32 result = false; + GB_ASSERT_NOT_NULL(path); +#if defined(GB_SYSTEM_WINDOWS) + result == (gb_strlen(path) > 2) && + gb_char_is_alpha(path[0]) && + (path[1] == ':' && path[2] == GB_PATH_SEPARATOR); +#else + result = (gb_strlen(path) > 0 && path[0] == GB_PATH_SEPARATOR); +#endif + return result; +} + +gb_inline b32 gb_path_is_relative(char const *path) { return !gb_path_is_absolute(path); } + +gb_inline b32 +gb_path_is_root(char const *path) +{ + b32 result = false; + GB_ASSERT_NOT_NULL(path); +#if defined(GB_SYSTEM_WINDOWS) + result = gb_path_is_absolute(path) && gb_strlen(path) == 3; +#else + result = gb_path_is_absolute(path) && gb_strlen(path) == 1; +#endif + return result; } + +gb_inline char const * +gb_path_base_name(char const *path) +{ + char const *ls; + GB_ASSERT_NOT_NULL(path); + ls = gb_char_last_occurence(path, '/'); + return (ls == NULL) ? path : ls+1; +} + +gb_inline char const * +gb_path_extension(char const *path) +{ + char const *ld; + GB_ASSERT_NOT_NULL(path); + ld = gb_char_last_occurence(path, '.'); + return (ld == NULL) ? NULL : ld+1; +} + +#if defined(GB_SYSTEM_WINDOWS) +gb_inline void gb_exit(u32 code) { ExitProcess(code); } +#else +#error +#endif + + +//////////////////////////////////////////////////////////////// +// +// DLL Handling +// +// + +#if defined(GB_SYSTEM_WINDOWS) + +gbDllHandle +gb_dll_load(char const *filepath, ...) +{ + gb_local_persist char buffer[512]; + va_list va; + va_start(va, filepath); + gb_snprintf_va(buffer, gb_size_of(buffer), filepath, va); + va_end(va); + return cast(gbDllHandle)LoadLibraryA(buffer); +} +gb_inline void gb_dll_unload(gbDllHandle dll) { FreeLibrary(cast(HMODULE)dll); } +gb_inline gbDllProc gb_dll_proc_address(gbDllHandle dll, char const *proc_name) { return cast(gbDllProc)GetProcAddress(cast(HMODULE)dll, proc_name); } + +#else +#error #endif -#endif // GB_INCLUDE_GB_H + +//////////////////////////////////////////////////////////////// +// +// Time +// +// +#if defined(GB_SYSTEM_WINDOWS) + +gb_inline u64 gb_rdtsc(void) { return __rdtsc(); } + +gb_global LARGE_INTEGER gb__win32_perf_count_freq = {0}; + +gb_inline f64 +gb_time_now(void) +{ + f64 result; + LARGE_INTEGER counter; + if (!gb__win32_perf_count_freq.QuadPart) + QueryPerformanceFrequency(&gb__win32_perf_count_freq); + GB_ASSERT(gb__win32_perf_count_freq.QuadPart != 0); + + QueryPerformanceCounter(&counter); + + result = counter.QuadPart / cast(f64)(gb__win32_perf_count_freq.QuadPart); + return result; +} + + +gb_inline void +gb_get_system_date(gbDate *date) +{ + SYSTEMTIME st = {0}; + GetSystemTime(&st); + date->year = st.wYear; + date->month = st.wMonth; + date->day_of_week = st.wDayOfWeek; + date->day = st.wDay; + date->hour = st.wHour; + date->minute = st.wMinute; + date->second = st.wSecond; + date->milliseconds = st.wMilliseconds; +} + +gb_inline void +gb_get_local_date(gbDate *date) +{ + SYSTEMTIME st = {0}; + GetLocalTime(&st); + date->year = st.wYear; + date->month = st.wMonth; + date->day_of_week = st.wDayOfWeek; + date->day = st.wDay; + date->hour = st.wHour; + date->minute = st.wMinute; + date->second = st.wSecond; + date->milliseconds = st.wMilliseconds; +} + +#else +#error +#endif + + + +#if defined(__cplusplus) +} +#endif +#endif /* GB_IMPLEMENTATION */ |
