2016-04-23 18:07:15 -04:00
|
|
|
/* 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
|
2016-03-03 06:37:48 -05:00
|
|
|
|
|
|
|
LICENSE
|
2016-04-23 18:07:15 -04:00
|
|
|
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.
|
2016-03-03 06:37:48 -05:00
|
|
|
|
|
|
|
WARNING
|
2016-04-23 18:07:15 -04:00
|
|
|
- This library is _slightly_ experimental and features may not work as expected.
|
2016-03-03 06:37:48 -05:00
|
|
|
- This also means that many functions are not documented.
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
CREDITS
|
|
|
|
Written by Ginger Bill
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2015-12-14 15:18:03 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
2015-12-14 10:42:52 -05:00
|
|
|
#ifndef GB_INCLUDE_GB_H
|
|
|
|
#define GB_INCLUDE_GB_H
|
|
|
|
|
|
|
|
#if defined(__cplusplus)
|
|
|
|
extern "C" {
|
2015-12-14 15:18:03 -05:00
|
|
|
#endif
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#ifndef GB_EXTERN
|
|
|
|
#if defined(__cplusplus)
|
|
|
|
#define GB_EXTERN extern "C"
|
|
|
|
#else
|
|
|
|
#define GB_EXTERN extern
|
|
|
|
#endif
|
2015-12-17 17:56:29 -05:00
|
|
|
#endif
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#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
|
2015-12-14 10:42:52 -05:00
|
|
|
#else
|
2016-04-23 18:07:15 -04:00
|
|
|
#define GB_DEF extern
|
2015-12-14 10:42:52 -05:00
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#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
|
2016-03-03 06:37:48 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
// 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
|
2015-12-14 19:18:52 -05:00
|
|
|
#endif
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#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)
|
2015-12-14 10:42:52 -05:00
|
|
|
#endif
|
|
|
|
|
2015-12-14 19:18:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#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
|
2015-12-14 19:18:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#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
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// 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
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#ifndef gb_malloc
|
|
|
|
#define gb_malloc(sz) malloc(sz)
|
|
|
|
#endif
|
2016-03-03 06:37:48 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#ifndef gb_mfree
|
|
|
|
#define gb_mfree(ptr) free(ptr)
|
|
|
|
#endif
|
2016-03-03 06:37:48 -05:00
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Base Types
|
|
|
|
//
|
|
|
|
//
|
2016-03-03 06:37:48 -05:00
|
|
|
|
2015-12-14 10:42:52 -05:00
|
|
|
#if defined(_MSC_VER)
|
2016-04-23 18:07:15 -04:00
|
|
|
typedef unsigned __int8 u8;
|
|
|
|
typedef signed __int8 i8;
|
2015-12-14 10:42:52 -05:00
|
|
|
typedef unsigned __int16 u16;
|
2016-04-23 18:07:15 -04:00
|
|
|
typedef signed __int16 i16;
|
2015-12-14 10:42:52 -05:00
|
|
|
typedef unsigned __int32 u32;
|
2016-04-23 18:07:15 -04:00
|
|
|
typedef signed __int32 i32;
|
2015-12-14 10:42:52 -05:00
|
|
|
typedef unsigned __int64 u64;
|
2016-04-23 18:07:15 -04:00
|
|
|
typedef signed __int64 i64;
|
2015-12-14 10:42:52 -05:00
|
|
|
#else
|
2015-12-14 19:18:52 -05:00
|
|
|
#include <stdint.h>
|
2016-04-23 18:07:15 -04:00
|
|
|
typedef uint8_t u8;
|
|
|
|
typedef int8_t i8;
|
2015-12-14 10:42:52 -05:00
|
|
|
typedef uint16_t u16;
|
2016-04-23 18:07:15 -04:00
|
|
|
typedef int16_t i16;
|
2015-12-14 10:42:52 -05:00
|
|
|
typedef uint32_t u32;
|
2016-04-23 18:07:15 -04:00
|
|
|
typedef int32_t i32;
|
2015-12-14 10:42:52 -05:00
|
|
|
typedef uint64_t u64;
|
2016-04-23 18:07:15 -04:00
|
|
|
typedef int64_t i64;
|
2015-12-14 10:42:52 -05:00
|
|
|
#endif
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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));
|
|
|
|
|
|
|
|
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;
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
GB_STATIC_ASSERT(sizeof(usize) == sizeof(isize));
|
2016-01-01 14:33:06 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
typedef uintptr_t uintptr;
|
2016-04-23 18:07:15 -04:00
|
|
|
typedef intptr_t intptr;
|
2016-01-01 14:33:06 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
typedef float f32;
|
|
|
|
typedef double f64;
|
2016-01-01 14:33:06 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#ifndef U8_MIN
|
2015-12-14 10:42:52 -05:00
|
|
|
#define U8_MIN 0u
|
|
|
|
#define U8_MAX 0xffu
|
2016-04-23 18:07:15 -04:00
|
|
|
#define I8_MIN (-0x7f - 1)
|
|
|
|
#define I8_MAX 0x7f
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
#define U16_MIN 0u
|
|
|
|
#define U16_MAX 0xffffu
|
2016-04-23 18:07:15 -04:00
|
|
|
#define I16_MIN (-0x7fff - 1)
|
|
|
|
#define I16_MAX 0x7fff
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
#define U32_MIN 0u
|
|
|
|
#define U32_MAX 0xffffffffu
|
2016-04-23 18:07:15 -04:00
|
|
|
#define I32_MIN (-0x7fffffff - 1)
|
|
|
|
#define I32_MAX 0x7fffffff
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
#define U64_MIN 0ull
|
|
|
|
#define U64_MAX 0xffffffffffffffffull
|
2016-04-23 18:07:15 -04:00
|
|
|
#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
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#define ISIZE_MIX I64_MIN
|
|
|
|
#define ISIZE_MAX I64_MAX
|
|
|
|
#else
|
|
|
|
#error Unknown architecture size
|
|
|
|
#endif
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#define F32_MIN 1.17549435e-38f
|
|
|
|
#define F32_MAX 3.40282347e+38f
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#define F64_MIN 2.2250738585072014e-308
|
|
|
|
#define F64_MAX 1.7976931348623157e+308
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
#endif
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
|
2015-12-14 19:18:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
#ifndef NULL
|
2016-04-23 18:07:15 -04:00
|
|
|
#if defined(__cplusplus)
|
|
|
|
#if __cplusplus >= 201103L
|
|
|
|
#define NULL nullptr
|
|
|
|
#else
|
|
|
|
#define NULL 0
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
#define NULL ((void *)0)
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if !defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER <= 1800
|
|
|
|
#define inline __inline
|
2016-03-03 06:37:48 -05:00
|
|
|
#endif
|
2016-01-01 14:33:06 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#if !defined(gb_inline)
|
|
|
|
#if defined(_MSC_VER)
|
|
|
|
#define gb_restrict __restrict
|
|
|
|
#else
|
|
|
|
#define gb_restrict restrict
|
|
|
|
#endif
|
2016-01-01 14:33:06 -05:00
|
|
|
#endif
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
// TODO(bill): Should force inline be a separate keyword and gb_inline be inline?
|
|
|
|
#if !defined(gb_inline)
|
2016-03-03 06:37:48 -05:00
|
|
|
#if defined(_MSC_VER)
|
|
|
|
#define gb_inline __forceinline
|
2016-01-01 14:33:06 -05:00
|
|
|
#else
|
2016-03-03 06:37:48 -05:00
|
|
|
#define gb_inline __attribute__ ((__always_inline__))
|
2015-12-17 17:56:29 -05:00
|
|
|
#endif
|
2015-12-14 19:18:52 -05:00
|
|
|
#endif
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#if !defined(gb_no_inline)
|
|
|
|
#if defined(_MSC_VER)
|
|
|
|
#define gb_no_inline __declspec(noinline)
|
|
|
|
#else
|
|
|
|
#define gb_no_inline __attribute__ ((noinline))
|
|
|
|
#endif
|
|
|
|
#endif
|
2015-12-14 19:18:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
// NOTE(bill): Easy to grep
|
|
|
|
// NOTE(bill): Not needed in macros
|
|
|
|
#ifndef cast
|
|
|
|
#define cast(Type) (Type)
|
|
|
|
#endif
|
2015-12-17 17:56:29 -05:00
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
// NOTE(bill): Because a signed sizeof is more useful
|
|
|
|
#ifndef gb_size_of
|
|
|
|
#define gb_size_of(x) (isize)(sizeof(x))
|
|
|
|
#endif
|
2015-12-17 17:56:29 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#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
|
2015-12-17 17:56:29 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#ifndef gb_offset_of
|
|
|
|
#define gb_offset_of(Type, element) ((isize)&(((Type *)0)->element))
|
|
|
|
#endif
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#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
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
// 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
|
2016-03-03 06:37:48 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
// 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
|
2015-12-17 17:56:29 -05:00
|
|
|
#endif
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#ifndef gb_unused
|
|
|
|
#define gb_unused(x) ((void)(gb_size_of(x)))
|
|
|
|
#endif
|
2016-03-03 06:37:48 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// 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
|
2016-03-03 06:37:48 -05:00
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
// Example
|
|
|
|
#if 0
|
|
|
|
gbMutex m;
|
|
|
|
gb_mutex_init(&m);
|
|
|
|
{
|
|
|
|
gb_mutex_lock(&m);
|
|
|
|
defer (gb_mutex_unlock(&m));
|
|
|
|
|
|
|
|
...
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
////////////////////////////////
|
|
|
|
//
|
|
|
|
// 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
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#ifndef GB_BIT
|
|
|
|
#define GB_BIT(x) (1<<x)
|
|
|
|
#endif
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#ifndef gb_min
|
|
|
|
#define gb_min(a, b) ((a) < (b) ? (a) : (b))
|
|
|
|
#endif
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#ifndef gb_max
|
|
|
|
#define gb_max(a, b) ((a) > (b) ? (a) : (b))
|
|
|
|
#endif
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#ifndef gb_clamp
|
|
|
|
#define gb_clamp(x, lower, upper) gb_min(gb_max((x), (lower)), (upper))
|
|
|
|
#endif
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#ifndef gb_clamp01
|
|
|
|
#define gb_clamp01(x) gb_clamp((x), 0, 1)
|
|
|
|
#endif
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
////////////////////////////////
|
2016-04-23 18:07:15 -04:00
|
|
|
//
|
|
|
|
// 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, ...);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
////////////////////////////////
|
2016-04-23 18:07:15 -04:00
|
|
|
//
|
|
|
|
// 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
|
2016-03-03 06:37:48 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
// TODO(bill): Should I completely rename these functions as they are a little weird to begin with?
|
|
|
|
|
|
|
|
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);
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
GB_DEF void gb_zero_size(void *ptr, isize size);
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#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);
|
2015-12-17 07:26:24 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
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;
|
2016-03-03 06:37:48 -05:00
|
|
|
void *data;
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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);
|
2015-12-14 10:42:52 -05:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// 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;
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#ifndef GB_DEFAULT_MEMORY_ALIGNMENT
|
|
|
|
#define GB_DEFAULT_MEMORY_ALIGNMENT 4
|
|
|
|
#endif
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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);
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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);
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
GB_DEF char *gb_alloc_cstring(gbAllocator a, char const *str);
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
|
|
|
|
// 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;
|
2016-03-03 06:37:48 -05:00
|
|
|
void *physical_start;
|
2016-04-23 18:07:15 -04:00
|
|
|
isize total_size;
|
|
|
|
isize total_allocated;
|
2016-03-03 06:37:48 -05:00
|
|
|
u32 temp_count;
|
2016-04-23 18:07:15 -04:00
|
|
|
} gbArena;
|
2016-03-03 06:37:48 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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);
|
2016-03-03 06:37:48 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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);
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
|
|
|
|
|
|
|
|
typedef struct gbPool {
|
|
|
|
gbAllocator backing;
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
void *physical_start;
|
|
|
|
void *free_list;
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
isize block_size;
|
|
|
|
isize block_align;
|
|
|
|
isize total_size;
|
|
|
|
} gbPool;
|
2016-03-03 06:37:48 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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);
|
2016-03-03 06:37:48 -05:00
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
GB_DEF gbAllocator gb_pool_allocator(gbPool *pool);
|
|
|
|
GB_DEF GB_ALLOCATOR_PROC(gb_pool_allocator_proc);
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
2015-12-14 18:53:20 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// 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");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gb_inline void *
|
|
|
|
gb_align_forward(void *ptr, isize align)
|
|
|
|
{
|
|
|
|
uintptr p;
|
|
|
|
isize modulo;
|
|
|
|
|
|
|
|
GB_ASSERT(gb_is_power_of_two(align));
|
|
|
|
|
|
|
|
p = cast(uintptr)ptr;
|
|
|
|
modulo = p % align;
|
|
|
|
if (modulo) p += (align - modulo);
|
|
|
|
return cast(void *)p;
|
|
|
|
}
|
|
|
|
|
|
|
|
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); }
|
|
|
|
|
|
|
|
|
|
|
|
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); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); }
|
|
|
|
|
|
|
|
gb_inline char *
|
|
|
|
gb_alloc_cstring(gbAllocator a, char const *str)
|
|
|
|
{
|
|
|
|
char *result;
|
|
|
|
isize len = gb_strlen(str);
|
|
|
|
result = cast(char *)gb_alloc_copy(a, str, len+1);
|
|
|
|
result[len] = '\0';
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Concurrency
|
|
|
|
//
|
|
|
|
|
|
|
|
#if defined(_MSC_VER)
|
|
|
|
gb_inline i32
|
|
|
|
gb_atomic32_load(gbAtomic32 const volatile *a)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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_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 i64
|
|
|
|
gb_atomic64_compare_exchange_strong(gbAtomic64 volatile *a, i64 expected, i64 desired)
|
|
|
|
{
|
|
|
|
return _InterlockedCompareExchange64(cast(i64 volatile *)a, desired, expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
|
|
|
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_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);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gb_thread_destory(gbThread *t)
|
|
|
|
{
|
|
|
|
if (t->is_running) gb_thread_join(t);
|
|
|
|
gb_semaphore_destroy(&t->semaphore);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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_ASSERT(!t->is_running);
|
|
|
|
GB_ASSERT(proc != NULL);
|
|
|
|
t->proc = proc;
|
|
|
|
t->data = data;
|
|
|
|
t->stack_size = stack_size;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gb_inline void
|
|
|
|
gb_arena_init_from_memory(gbArena *arena, void *start, isize size)
|
|
|
|
{
|
|
|
|
arena->backing.proc = NULL;
|
|
|
|
arena->backing.data = NULL;
|
|
|
|
arena->physical_start = start;
|
|
|
|
arena->total_size = size;
|
|
|
|
arena->total_allocated = 0;
|
|
|
|
arena->temp_count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
gb_inline void
|
|
|
|
gb_arena_init_from_allocator(gbArena *arena, gbAllocator backing, isize size)
|
|
|
|
{
|
|
|
|
arena->backing = backing;
|
|
|
|
arena->physical_start = gb_alloc(backing, size); // NOTE(bill): Uses default alignment
|
|
|
|
arena->total_size = size;
|
|
|
|
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_arena_free(gbArena *arena)
|
|
|
|
{
|
|
|
|
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;
|
2015-12-14 18:53:20 -05:00
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline isize
|
|
|
|
gb_arena_size_remaining(gbArena *arena, isize alignment)
|
2015-12-14 18:53:20 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
isize result = arena->total_size - (arena->total_allocated + gb_arena_alignment_of(arena, alignment));
|
|
|
|
return result;
|
2015-12-14 18:53:20 -05:00
|
|
|
}
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline void gb_arena_check(gbArena *arena) { GB_ASSERT(arena->temp_count == 0); }
|
2016-03-03 06:37:48 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gb_inline gbAllocator
|
|
|
|
gb_arena_allocator(gbArena *arena)
|
2015-12-14 18:53:20 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
gbAllocator allocator;
|
|
|
|
allocator.proc = gb_arena_allocator_proc;
|
2016-03-03 06:37:48 -05:00
|
|
|
allocator.data = arena;
|
|
|
|
return allocator;
|
2015-12-14 18:53:20 -05:00
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
GB_ALLOCATOR_PROC(gb_arena_allocator_proc)
|
2015-12-14 18:53:20 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
gbArena *arena = cast(gbArena *)allocator_data;
|
2016-03-03 06:37:48 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_unused(options);
|
|
|
|
gb_unused(old_size);
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
switch (type) {
|
2016-04-23 18:07:15 -04:00
|
|
|
case GB_ALLOCATION_ALLOC: {
|
2016-03-03 06:37:48 -05:00
|
|
|
void *ptr;
|
2016-04-23 18:07:15 -04:00
|
|
|
isize actual_size = size + alignment;
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
// NOTE(bill): Out of memory
|
2016-04-23 18:07:15 -04:00
|
|
|
if (arena->total_allocated + actual_size > cast(isize)arena->total_size)
|
2016-03-03 06:37:48 -05:00
|
|
|
return NULL;
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
ptr = gb_align_forward(gb_pointer_add(arena->physical_start, arena->total_allocated), alignment);
|
|
|
|
arena->total_allocated += actual_size;
|
2016-03-03 06:37:48 -05:00
|
|
|
return ptr;
|
|
|
|
} break;
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
case GB_ALLOCATION_FREE:
|
2016-03-03 06:37:48 -05:00
|
|
|
// NOTE(bill): Free all at once
|
|
|
|
// NOTE(bill): Use Temp_Arena_Memory if you want to free a block
|
2016-04-23 18:07:15 -04:00
|
|
|
// TODO(bill): Free it if it's on top of the stack
|
|
|
|
break;
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
case GB_ALLOCATION_FREE_ALL:
|
|
|
|
arena->total_allocated = 0;
|
2016-03-03 06:37:48 -05:00
|
|
|
break;
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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;
|
|
|
|
}
|
2016-03-03 06:37:48 -05:00
|
|
|
} break;
|
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
return NULL; // NOTE(bill): Default return value
|
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline gbTempArenaMemory
|
|
|
|
gb_temp_arena_memory_begin(gbArena *arena)
|
2015-12-17 07:26:24 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
gbTempArenaMemory tmp;
|
2016-03-03 06:37:48 -05:00
|
|
|
tmp.arena = arena;
|
2016-04-23 18:07:15 -04:00
|
|
|
tmp.original_count = arena->total_allocated;
|
2016-03-03 06:37:48 -05:00
|
|
|
arena->temp_count++;
|
|
|
|
return tmp;
|
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
gb_inline void
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_temp_arena_memory_end(gbTempArenaMemory tmp)
|
2016-03-03 06:37:48 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
GB_ASSERT(tmp.arena->total_allocated >= tmp.original_count);
|
2016-03-03 06:37:48 -05:00
|
|
|
GB_ASSERT(tmp.arena->temp_count > 0);
|
2016-04-23 18:07:15 -04:00
|
|
|
tmp.arena->total_allocated = tmp.original_count;
|
2016-03-03 06:37:48 -05:00
|
|
|
tmp.arena->temp_count--;
|
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline void
|
|
|
|
gb_pool_init(gbPool *pool, gbAllocator backing, isize num_blocks, isize block_size)
|
2015-12-17 07:26:24 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_pool_init_align(pool, backing, num_blocks, block_size, GB_DEFAULT_MEMORY_ALIGNMENT);
|
2016-03-03 06:37:48 -05:00
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
void
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_pool_init_align(gbPool *pool, gbAllocator backing, isize num_blocks, isize block_size, isize block_align)
|
2016-03-03 06:37:48 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
isize actual_block_size, pool_size, block_index;
|
|
|
|
void *data, *curr;
|
|
|
|
uintptr *end;
|
|
|
|
|
|
|
|
gb_zero_struct(pool);
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
pool->backing = backing;
|
|
|
|
pool->block_size = block_size;
|
|
|
|
pool->block_align = block_align;
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
actual_block_size = block_size + block_align;
|
|
|
|
pool_size = num_blocks * actual_block_size;
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
data = gb_alloc_align(backing, pool_size, block_align);
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
// NOTE(bill): Init intrusive freelist
|
|
|
|
curr = data;
|
|
|
|
for (block_index = 0; block_index < num_blocks-1; block_index++) {
|
2016-03-03 06:37:48 -05:00
|
|
|
uintptr *next = cast(uintptr *)curr;
|
|
|
|
*next = cast(uintptr)curr + actual_block_size;
|
2016-04-23 18:07:15 -04:00
|
|
|
curr = gb_pointer_add(curr, actual_block_size);
|
2016-03-03 06:37:48 -05:00
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
end = cast(uintptr *)curr;
|
2016-03-03 06:37:48 -05:00
|
|
|
*end = cast(uintptr)NULL;
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
pool->physical_start = data;
|
|
|
|
pool->free_list = data;
|
|
|
|
}
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline void
|
|
|
|
gb_pool_free(gbPool *pool)
|
2016-03-03 06:37:48 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
if (pool->backing.proc) {
|
2016-03-03 06:37:48 -05:00
|
|
|
gb_free(pool->backing, pool->physical_start);
|
|
|
|
}
|
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline gbAllocator
|
|
|
|
gb_pool_allocator(gbPool *pool)
|
2016-03-03 06:37:48 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
gbAllocator allocator;
|
|
|
|
allocator.proc = gb_pool_allocator_proc;
|
2016-03-03 06:37:48 -05:00
|
|
|
allocator.data = pool;
|
|
|
|
return allocator;
|
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
GB_ALLOCATOR_PROC(gb_pool_allocator_proc)
|
2016-03-03 06:37:48 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
gbPool *pool = cast(gbPool *)allocator_data;
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_unused(options);
|
|
|
|
gb_unused(old_size);
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
switch (type) {
|
2016-04-23 18:07:15 -04:00
|
|
|
case GB_ALLOCATION_ALLOC: {
|
2016-03-03 06:37:48 -05:00
|
|
|
uintptr next_free;
|
|
|
|
void *ptr;
|
|
|
|
GB_ASSERT(size == pool->block_size);
|
|
|
|
GB_ASSERT(alignment == pool->block_align);
|
|
|
|
GB_ASSERT(pool->free_list != NULL);
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
next_free = *cast(uintptr *)pool->free_list;
|
|
|
|
ptr = pool->free_list;
|
|
|
|
pool->free_list = cast(void *)next_free;
|
|
|
|
pool->total_size += pool->block_size;
|
|
|
|
return ptr;
|
|
|
|
} break;
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
case GB_ALLOCATION_FREE: {
|
2016-03-03 06:37:48 -05:00
|
|
|
uintptr *next;
|
|
|
|
if (old_memory == NULL) return NULL;
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
next = cast(uintptr *)old_memory;
|
|
|
|
*next = cast(uintptr)pool->free_list;
|
|
|
|
pool->free_list = old_memory;
|
|
|
|
pool->total_size -= pool->block_size;
|
|
|
|
} break;
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
case GB_ALLOCATION_FREE_ALL:
|
2016-03-03 06:37:48 -05:00
|
|
|
// TODO(bill):
|
2016-04-23 18:07:15 -04:00
|
|
|
break;
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
case GB_ALLOCATION_RESIZE:
|
2016-03-03 06:37:48 -05:00
|
|
|
// NOTE(bill): Cannot resize
|
2016-04-23 18:07:15 -04:00
|
|
|
break;
|
2016-03-03 06:37:48 -05:00
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
return NULL;
|
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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;
|
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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;
|
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline b32
|
|
|
|
gb_char_is_alpha(char c)
|
|
|
|
{
|
|
|
|
if ((c >= 'A' && c <= 'Z') ||
|
|
|
|
(c >= 'a' && c <= 'z'))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline b32
|
|
|
|
gb_char_is_alphanumeric(char c)
|
|
|
|
{
|
|
|
|
return gb_char_is_alpha(c) || gb_char_is_digit(c);
|
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline void
|
|
|
|
gb_to_lower(char *str)
|
|
|
|
{
|
|
|
|
if (!str) return;
|
|
|
|
while (*str) {
|
|
|
|
*str = gb_char_to_lower(*str);
|
|
|
|
str++;
|
|
|
|
}
|
|
|
|
}
|
2016-03-03 06:37:48 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline void
|
|
|
|
gb_to_upper(char *str)
|
|
|
|
{
|
|
|
|
if (!str) return;
|
|
|
|
while (*str) {
|
|
|
|
*str = gb_char_to_upper(*str);
|
|
|
|
str++;
|
|
|
|
}
|
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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;
|
|
|
|
}
|
2016-03-03 06:37:48 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline i32
|
|
|
|
gb_strcmp(char const *s1, char const *s2)
|
|
|
|
{
|
|
|
|
while (*s1 && (*s1 == *s2)) {
|
|
|
|
s1++, s2++;
|
|
|
|
}
|
|
|
|
return *(u8 *)s1 - *(u8 *)s2;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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_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_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)
|
2015-12-17 07:26:24 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
char const *result = NULL;
|
|
|
|
do {
|
|
|
|
if (*s == c)
|
|
|
|
result = s;
|
|
|
|
} while (*s++);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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';
|
|
|
|
}
|
|
|
|
}
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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; }
|
2015-12-14 10:42:52 -05:00
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline gbString
|
|
|
|
gb_string_make(gbAllocator a, char const *str)
|
2016-03-03 06:37:48 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
isize len = str ? gb_strlen(str) : 0;
|
2016-03-03 06:37:48 -05:00
|
|
|
return gb_string_make_length(a, str, len);
|
|
|
|
}
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gbString
|
|
|
|
gb_string_make_length(gbAllocator a, void const *init_str, isize num_bytes)
|
2016-03-03 06:37:48 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
isize header_size = gb_size_of(gbStringHeader);
|
2016-03-03 06:37:48 -05:00
|
|
|
void *ptr = gb_alloc(a, header_size + num_bytes + 1);
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gbString str;
|
|
|
|
gbStringHeader *header;
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
if (!init_str) gb_zero_size(ptr, header_size + num_bytes + 1);
|
|
|
|
if (ptr == NULL) return NULL;
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
str = cast(char *)ptr + header_size;
|
|
|
|
header = GB_STRING_HEADER(str);
|
|
|
|
header->allocator = a;
|
|
|
|
header->length = num_bytes;
|
|
|
|
header->capacity = num_bytes;
|
|
|
|
if (num_bytes && init_str)
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_memcopy(str, init_str, num_bytes);
|
2016-03-03 06:37:48 -05:00
|
|
|
str[num_bytes] = '\0';
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
return str;
|
|
|
|
}
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
gb_inline void
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_string_free(gbString str)
|
2016-03-03 06:37:48 -05:00
|
|
|
{
|
|
|
|
if (str) {
|
2016-04-23 18:07:15 -04:00
|
|
|
gbStringHeader *header = GB_STRING_HEADER(str);
|
2016-03-03 06:37:48 -05:00
|
|
|
gb_free(header->allocator, header);
|
|
|
|
}
|
|
|
|
}
|
2015-12-14 15:18:03 -05:00
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline gbString gb_string_duplicate(gbAllocator a, gbString const str) { return gb_string_make_length(a, str, gb_string_length(str)); }
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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; }
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline isize
|
|
|
|
gb_string_available_space(gbString const str)
|
2015-12-17 17:56:29 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
gbStringHeader *h = GB_STRING_HEADER(str);
|
2016-03-03 06:37:48 -05:00
|
|
|
if (h->capacity > h->length)
|
|
|
|
return h->capacity - h->length;
|
|
|
|
return 0;
|
|
|
|
}
|
2015-12-14 15:18:03 -05:00
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline void gb_string_clear(gbString str) { gb__string_set_length(str, 0); str[0] = '\0'; }
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline gbString gb_string_append_string(gbString str, gbString const other) { return gb_string_append_string_length(str, other, gb_string_length(other)); }
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gbString
|
|
|
|
gb_string_append_string_length(gbString str, void const *other, isize other_len)
|
2016-03-03 06:37:48 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
isize curr_len = gb_string_length(str);
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
str = gb_string_make_space_for(str, other_len);
|
|
|
|
if (str == NULL)
|
|
|
|
return NULL;
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_memcopy(str + curr_len, other, other_len);
|
2016-03-03 06:37:48 -05:00
|
|
|
str[curr_len + other_len] = '\0';
|
|
|
|
gb__string_set_length(str, curr_len + other_len);
|
2015-12-14 10:42:52 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
return str;
|
2015-12-14 10:42:52 -05:00
|
|
|
}
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline gbString
|
|
|
|
gb_string_append_cstring(gbString str, char const *other)
|
2015-12-14 15:18:03 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
return gb_string_append_string_length(str, other, cast(isize)strlen(other));
|
2015-12-14 15:18:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gbString
|
|
|
|
gb_string_set(gbString str, char const *cstr)
|
2015-12-14 15:18:03 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
isize len = gb_strlen(cstr);
|
2016-03-03 06:37:48 -05:00
|
|
|
if (gb_string_capacity(str) < len) {
|
2015-12-14 15:18:03 -05:00
|
|
|
str = gb_string_make_space_for(str, len - gb_string_length(str));
|
|
|
|
if (str == NULL)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_memcopy(str, cstr, len);
|
2015-12-14 15:18:03 -05:00
|
|
|
str[len] = '\0';
|
|
|
|
gb__string_set_length(str, len);
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gbString
|
|
|
|
gb_string_make_space_for(gbString str, isize add_len)
|
2015-12-14 15:18:03 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
isize available = gb_string_available_space(str);
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
// Return if there is enough space left
|
|
|
|
if (available >= add_len) {
|
|
|
|
return str;
|
|
|
|
} else {
|
2016-04-23 18:07:15 -04:00
|
|
|
isize new_len = gb_string_length(str) + add_len;
|
2016-03-03 06:37:48 -05:00
|
|
|
void *ptr = GB_STRING_HEADER(str);
|
2016-04-23 18:07:15 -04:00
|
|
|
isize old_size = gb_size_of(gbStringHeader) + gb_string_length(str) + 1;
|
|
|
|
isize new_size = gb_size_of(gbStringHeader) + new_len + 1;
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
void *new_ptr = gb_resize(GB_STRING_HEADER(str)->allocator, ptr, old_size, new_size);
|
2016-03-03 06:37:48 -05:00
|
|
|
if (new_ptr == NULL) return NULL;
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
str = cast(char *)(GB_STRING_HEADER(new_ptr) + 1);
|
|
|
|
gb__string_set_capacity(str, new_len);
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
return str;
|
|
|
|
}
|
2015-12-14 15:18:03 -05:00
|
|
|
}
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_inline isize
|
|
|
|
gb_string_allocation_size(gbString const str)
|
2015-12-14 15:18:03 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
isize cap = gb_string_capacity(str);
|
|
|
|
return gb_size_of(gbStringHeader) + cap;
|
2015-12-14 15:18:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
gb_inline b32
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_strings_are_equal(gbString const lhs, gbString const rhs)
|
2015-12-14 15:18:03 -05:00
|
|
|
{
|
2016-04-23 18:07:15 -04:00
|
|
|
isize lhs_len, rhs_len, i;
|
2016-03-03 06:37:48 -05:00
|
|
|
lhs_len = gb_string_length(lhs);
|
|
|
|
rhs_len = gb_string_length(rhs);
|
2015-12-14 15:18:03 -05:00
|
|
|
if (lhs_len != rhs_len)
|
|
|
|
return false;
|
|
|
|
|
2016-03-03 06:37:48 -05:00
|
|
|
for (i = 0; i < lhs_len; i++) {
|
2015-12-14 15:18:03 -05:00
|
|
|
if (lhs[i] != rhs[i])
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
gbString
|
|
|
|
gb_string_trim(gbString str, char const *cut_set)
|
2015-12-14 15:18:03 -05:00
|
|
|
{
|
2016-03-03 06:37:48 -05:00
|
|
|
char *start, *end, *start_pos, *end_pos;
|
2016-04-23 18:07:15 -04:00
|
|
|
isize len;
|
2015-12-14 15:18:03 -05:00
|
|
|
|
|
|
|
start_pos = start = str;
|
|
|
|
end_pos = end = str + gb_string_length(str) - 1;
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
while (start_pos <= end && gb_char_first_occurence(cut_set, *start_pos))
|
2015-12-14 15:18:03 -05:00
|
|
|
start_pos++;
|
2016-04-23 18:07:15 -04:00
|
|
|
while (end_pos > start_pos && gb_char_first_occurence(cut_set, *end_pos))
|
2015-12-14 15:18:03 -05:00
|
|
|
end_pos--;
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
len = cast(isize)((start_pos > end_pos) ? 0 : ((end_pos - start_pos)+1));
|
2015-12-14 15:18:03 -05:00
|
|
|
|
|
|
|
if (str != start_pos)
|
2016-04-23 18:07:15 -04:00
|
|
|
gb_memmove(str, start_pos, len);
|
2015-12-14 15:18:03 -05:00
|
|
|
str[len] = '\0';
|
|
|
|
|
|
|
|
gb__string_set_length(str, len);
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-12-14 15:18:03 -05:00
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
#else
|
|
|
|
#error
|
2015-12-14 15:18:03 -05:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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);
|
|
|
|
}
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// 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;
|
2015-12-14 15:18:03 -05:00
|
|
|
}
|
2016-04-23 18:07:15 -04:00
|
|
|
|
|
|
|
|
|
|
|
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
|
2016-03-03 06:37:48 -05:00
|
|
|
#endif
|
2015-12-14 15:18:03 -05:00
|
|
|
|
2016-04-23 18:07:15 -04:00
|
|
|
|
|
|
|
|
|
|
|
#if defined(__cplusplus)
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif /* GB_IMPLEMENTATION */
|