Scratch Memory Allocator

This commit is contained in:
gingerBill 2016-05-08 19:12:35 +01:00
parent ed22864d09
commit 9faf658509
1 changed files with 201 additions and 92 deletions

293
gb.h
View File

@ -1,4 +1,4 @@
/* gb.h - v0.09a - Ginger Bill's C Helper Library - public domain
/* gb.h - v0.10 - 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
@ -26,6 +26,7 @@ Conventions used:
Version History:
0.10 - Scratch Memory Allocator
0.09a - Faster Mutex and the Free List is slightly improved
0.09 - Basic Virtual Memory System and Dreadful Free List allocator
0.08a - Fix *_appendv bug
@ -575,7 +576,7 @@ namespace gb {
#ifndef GB_DEBUG_TRAP
#if defined(_MSC_VER)
#if _MSC_VER < 1300
#define GB_DEBUG_TRAP() __asm int 3; // Trap to debugger!
#define GB_DEBUG_TRAP() __asm int 3; /* Trap to debugger! */
#else
#define GB_DEBUG_TRAP() __debugbreak()
#endif
@ -655,6 +656,8 @@ GB_DEF void * gb_pointer_add (void *ptr, isize bytes);
GB_DEF void * gb_pointer_sub (void *ptr, isize bytes);
GB_DEF void const *gb_pointer_add_const(void const *ptr, isize bytes);
GB_DEF void const *gb_pointer_sub_const(void const *ptr, isize bytes);
GB_DEF isize gb_pointer_diff (void const *begin, void const *end);
GB_DEF void gb_zero_size(void *ptr, isize size);
#ifndef gb_zero_struct
@ -746,11 +749,11 @@ gb_mutex_init(&m);
/* TODO(bill): Should I create a Condition Type? (gbCond vs gbCondition) */
typedef struct gbSemaphore {
#if defined(GB_SYSTEM_WINDOWS)
void *win32_handle;
#else
/* TODO(bill): Use a "native" semaphore rather than this mutex/condition pair */
gbMutex mutex;
pthread_cond_t cond;
i32 count;
@ -951,6 +954,18 @@ GB_DEF GB_ALLOCATOR_PROC(gb_pool_allocator_proc);
/* NOTE(bill): Used for allocators to keep track of sizes */
typedef struct gbAllocationHeader {
isize size;
} gbAllocationHeader;
#define GB_ISIZE_HIGH_BIT (~(cast(isize)(-1)))
GB_DEF gbAllocationHeader *gb_allocation_header (void *data);
GB_DEF void gb_allocation_header_fill(gbAllocationHeader *header, void *data, isize size);
/*
* Free List Allocator
@ -962,9 +977,6 @@ GB_DEF GB_ALLOCATOR_PROC(gb_pool_allocator_proc);
*/
/* NOTE(bill): I may also complete remove this if I completely implement a fixed heap allocator */
typedef struct gbFreeListHeader {
isize size;
} gbFreeListHeader;
typedef struct gbFreeListBlock {
struct gbFreeListBlock *next;
@ -989,6 +1001,25 @@ GB_DEF gbAllocator gb_free_list_allocator(gbFreeList *fl);
GB_DEF GB_ALLOCATOR_PROC(gb_free_list_allocator_proc);
/*
* Scratch Memory Allocator - Ring Buffer Based Arena
*/
typedef struct gbScratchMemory {
void *physical_start;
isize total_size;
void *alloc_point, *free_point;
} gbScratchMemory;
GB_DEF void gb_scratch_memory_init (gbScratchMemory *s, void *start, isize size);
GB_DEF b32 gb_scratch_memory_is_in_use(gbScratchMemory *s, void *ptr);
/* Allocation Types: alloc, free, free_all, resize */
GB_DEF gbAllocator gb_scratch_allocator(gbScratchMemory *s);
GB_DEF GB_ALLOCATOR_PROC(gb_scratch_allocator_proc);
/* TODO(bill): Stack allocator */
/* TODO(bill): Fixed heap allocator */
/* TODO(bill): General heap allocator. Maybe a TCMalloc like clone? */
@ -1748,7 +1779,6 @@ extern "C" {
*/
#include <process.h>
// #include <direct.h>
#ifndef _WINDOWS_ /* check windows.h guard */
#define INFINITE 0xffffffff
@ -1764,23 +1794,23 @@ typedef u32 ULONG_PTR;
typedef union _ULARGE_INTEGER {
struct {
unsigned long LowPart;
unsigned long HighPart;
DWORD LowPart;
DWORD HighPart;
};
struct {
unsigned long LowPart;
unsigned long HighPart;
DWORD LowPart;
DWORD HighPart;
} u;
unsigned long long QuadPart;
DWORD long QuadPart;
} ULARGE_INTEGER;
typedef union _LARGE_INTEGER {
struct {
unsigned long LowPart;
DWORD LowPart;
long HighPart;
};
struct {
unsigned long LowPart;
DWORD LowPart;
long HighPart;
} u;
long long QuadPart;
@ -1792,33 +1822,33 @@ typedef enum _GET_FILEEX_INFO_LEVELS {
} GET_FILEEX_INFO_LEVELS;
typedef struct _FILETIME {
unsigned long dwLowDateTime;
unsigned long dwHighDateTime;
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME;
typedef struct _WIN32_FILE_ATTRIBUTE_DATA {
unsigned long dwFileAttributes;
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
unsigned long nFileSizeHigh;
unsigned long nFileSizeLow;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
} WIN32_FILE_ATTRIBUTE_DATA;
typedef unsigned long (__stdcall *gb__Win32ThreadProc)(void *arg);
typedef DWORD (__stdcall *gb__Win32ThreadProc)(void *arg);
GB_DLL_IMPORT b32 __stdcall CloseHandle(void *);
GB_DLL_IMPORT void __stdcall Sleep(unsigned long);
GB_DLL_IMPORT void __stdcall Sleep(DWORD);
GB_DLL_IMPORT void * __stdcall CreateSemaphoreA(void *sec, long,long,char*);
GB_DLL_IMPORT unsigned long __stdcall WaitForSingleObject(void *, unsigned long);
GB_DLL_IMPORT DWORD __stdcall WaitForSingleObject(void *, DWORD);
GB_DLL_IMPORT b32 __stdcall ReleaseSemaphore(void *, long, long *);
GB_DLL_IMPORT void * __stdcall CreateThread(void *, size_t, gb__Win32ThreadProc, void *, unsigned long, unsigned long *);
GB_DLL_IMPORT b32 __stdcall TerminateThread(void *, unsigned long);
GB_DLL_IMPORT unsigned long __stdcall GetCurrentThreadId(void);
GB_DLL_IMPORT unsigned long __stdcall GetThreadId(void *);
GB_DLL_IMPORT void __stdcall RaiseException(unsigned long, unsigned long, unsigned long, ULONG_PTR const *);
GB_DLL_IMPORT void * __stdcall VirtualAlloc(void *base_address, size_t size, unsigned long type, unsigned long protect);
GB_DLL_IMPORT b32 __stdcall VirtualFree(void *base_address, size_t size, unsigned long freetype);
GB_DLL_IMPORT void * __stdcall CreateThread(void *, size_t, gb__Win32ThreadProc, void *, DWORD, DWORD *);
GB_DLL_IMPORT b32 __stdcall TerminateThread(void *, DWORD);
GB_DLL_IMPORT DWORD __stdcall GetCurrentThreadId(void);
GB_DLL_IMPORT DWORD __stdcall GetThreadId(void *);
GB_DLL_IMPORT void __stdcall RaiseException(DWORD, DWORD, DWORD, ULONG_PTR const *);
GB_DLL_IMPORT void * __stdcall VirtualAlloc(void *base_address, size_t size, DWORD type, DWORD protect);
GB_DLL_IMPORT b32 __stdcall VirtualFree(void *base_address, size_t size, DWORD freetype);
GB_DLL_IMPORT b32 __stdcall GetFileAttributesExA(char const *filename, GET_FILEEX_INFO_LEVELS info_level_id, void *file_info);
@ -1981,10 +2011,11 @@ gb_align_forward(void *ptr, isize alignment)
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 const *gb_pointer_add_const(void const *ptr, isize bytes) { return cast(void const *)(cast(u8 const *)ptr + bytes); }
gb_inline void const *gb_pointer_sub_const(void const *ptr, isize bytes) { return cast(void const *)(cast(u8 const *)ptr - bytes); }
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 const *gb_pointer_add_const(void const *ptr, isize bytes) { return cast(void const *)(cast(u8 const *)ptr + bytes); }
gb_inline void const *gb_pointer_sub_const(void const *ptr, isize bytes) { return cast(void const *)(cast(u8 const *)ptr - bytes); }
gb_inline isize gb_pointer_diff (void const *begin, void const *end) { return cast(isize)(cast(u8 const *)end - cast(u8 const *)begin); }
gb_inline void gb_zero_size(void *ptr, isize size) { gb_memset(ptr, 0, size); }
@ -2043,42 +2074,28 @@ gb_default_resize_align(gbAllocator a, void *old_memory, isize old_size, isize n
*
*/
#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_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(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)
{
@ -2199,21 +2216,8 @@ gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand)
#else /* GCC */
gb_inline i32
gb_atomic32_load(gbAtomic32 const volatile *a)
{
#if defined(GB_ARCH_64_BIT)
return a->value;
#else
#endif
}
gb_inline void
gb_atomic32_store(gbAtomic32 volatile *a, i32 value)
{
a->value = value;
}
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(gbAtomic32 volatile *a, i32 expected, i32 desired)
@ -2463,8 +2467,8 @@ gb_mutex_lock(gbMutex *m)
gb_inline b32
gb_mutex_try_lock(gbMutex *m)
{
b32 result = 0 == gb_atomic32_compare_exchange(&m->counter, 1, 0);
return result;
i32 result = gb_atomic32_compare_exchange(&m->counter, 1, 0);
return (result == 0);
}
gb_inline void
@ -2539,7 +2543,7 @@ gb_semaphore_post(gbSemaphore *s, i32 count)
gb_inline void
gb_semaphore_wait(gbSemaphore *s)
{
unsigned long result = WaitForSingleObject(s->win32_handle, INFINITE);
DWORD result = WaitForSingleObject(s->win32_handle, INFINITE);
GB_ASSERT_MSG(result == 0, "WaitForSingleObject: GetLastError");
}
@ -2614,7 +2618,7 @@ gb__thread_run(gbThread *t)
}
#if defined(GB_SYSTEM_WINDOWS)
gb_inline unsigned long __stdcall gb__thread_proc(void *arg) { gb__thread_run(cast(gbThread *)arg); return 0; }
gb_inline DWORD __stdcall gb__thread_proc(void *arg) { gb__thread_run(cast(gbThread *)arg); return 0; }
#else
gb_inline void * gb__thread_proc(void *arg) { gb__thread_run(cast(gbThread *)arg); return NULL; }
#endif
@ -2695,10 +2699,10 @@ gb_thread_set_name(gbThread *t, char const *name)
/* TODO(bill): Bloody Windows!!! */
#pragma pack(push, 8)
struct gbprivThreadName {
unsigned long type;
DWORD type;
char const *name;
unsigned long id;
unsigned long flags;
DWORD id;
DWORD flags;
};
#pragma pack(pop)
struct gbprivThreadName tn;
@ -3092,6 +3096,26 @@ GB_ALLOCATOR_PROC(gb_pool_allocator_proc)
gb_inline gbAllocationHeader *
gb_allocation_header(void *data)
{
isize *p = cast(isize *)data;
while (p[-1] == cast(isize)(-1))
p--;
return cast(gbAllocationHeader *)p - 1;
}
gb_inline void gb_allocation_header_fill(gbAllocationHeader *header, void *data, isize size)
{
isize *ptr;
header->size = size;
ptr = cast(isize *)(header + 1);
while (cast(void *)ptr < data)
*ptr++ = cast(isize)(-1);
}
/*
* Free List Allocator
*/
@ -3127,15 +3151,6 @@ gb_free_list_allocator(gbFreeList *fl)
return a;
}
gb_inline gb_internal gbFreeListHeader *
gb__free_list_header(void *data)
{
isize *p = cast(isize *)data;
while (p[-1] == cast(isize)(-1))
p--;
return cast(gbFreeListHeader *)p - 1;
}
GB_ALLOCATOR_PROC(gb_free_list_allocator_proc)
{
@ -3150,9 +3165,9 @@ GB_ALLOCATOR_PROC(gb_free_list_allocator_proc)
while (curr_block) {
void *ptr = NULL;
isize total_size;
gbFreeListHeader *header;
gbAllocationHeader *header;
total_size = size + alignment + gb_size_of(gbFreeListHeader);
total_size = size + alignment + gb_size_of(gbAllocationHeader);
if (curr_block->size < total_size) {
prev_block = curr_block;
@ -3160,7 +3175,7 @@ GB_ALLOCATOR_PROC(gb_free_list_allocator_proc)
continue;
}
if (curr_block->size - total_size <= gb_size_of(gbFreeListHeader)) {
if (curr_block->size - total_size <= gb_size_of(gbAllocationHeader)) {
total_size = curr_block->size;
if (prev_block)
@ -3168,7 +3183,7 @@ GB_ALLOCATOR_PROC(gb_free_list_allocator_proc)
else
fl->curr_block = curr_block->next;
} else {
// NOTE(bill): Create a new block for the remaining memory
/* NOTE(bill): Create a new block for the remaining memory */
gbFreeListBlock *next_block;
next_block = cast(gbFreeListBlock *)gb_pointer_add(curr_block, total_size);
@ -3185,14 +3200,9 @@ GB_ALLOCATOR_PROC(gb_free_list_allocator_proc)
/* TODO(bill): Set Header Info */
header = cast(gbFreeListHeader *)curr_block;
header = cast(gbAllocationHeader *)curr_block;
ptr = gb_align_forward(header+1, alignment);
header->size = total_size;
{
isize *p = cast(isize *)(header+1);
while (cast(void *)p < cast(void *)ptr)
*p++ = cast(isize)(-1);
}
gb_allocation_header_fill(header, ptr, size);
fl->total_allocated += total_size;
fl->allocation_count++;
@ -3206,7 +3216,7 @@ GB_ALLOCATOR_PROC(gb_free_list_allocator_proc)
} break;
case GB_ALLOCATION_FREE: {
gbFreeListHeader *header = gb__free_list_header(old_memory);
gbAllocationHeader *header = gb_allocation_header(old_memory);
isize block_size = header->size;
uintptr block_start, block_end;
gbFreeListBlock *prev_block = NULL;
@ -3261,6 +3271,103 @@ GB_ALLOCATOR_PROC(gb_free_list_allocator_proc)
void
gb_scratch_memory_init(gbScratchMemory *s, void *start, isize size)
{
s->physical_start = start;
s->total_size = size;
s->alloc_point = start;
s->free_point = start;
}
b32
gb_scratch_memory_is_in_use(gbScratchMemory *s, void *ptr)
{
if (s->free_point == s->alloc_point) return false;
if (s->alloc_point > s->free_point)
return ptr >= s->free_point && ptr < s->alloc_point;
return ptr >= s->free_point || ptr < s->alloc_point;
}
gbAllocator
gb_scratch_allocator(gbScratchMemory *s)
{
gbAllocator a;
a.proc = gb_scratch_allocator_proc;
a.data = s;
return a;
}
GB_ALLOCATOR_PROC(gb_scratch_allocator_proc)
{
gbScratchMemory *s = cast(gbScratchMemory *)allocator_data;
GB_ASSERT_NOT_NULL(s);
switch (type) {
case GB_ALLOCATION_ALLOC: {
void *ptr = s->alloc_point;
gbAllocationHeader *header = cast(gbAllocationHeader *)ptr;
void *data = gb_align_forward(header+1, alignment);
void *end = gb_pointer_add(s->physical_start, s->total_size);
GB_ASSERT(alignment % 4 == 0);
size = ((size + 3)/4)*4;
ptr = gb_pointer_add(ptr, size);
/* NOTE(bill): Wrap around */
if (ptr > end) {
header->size = gb_pointer_diff(header, end) | GB_ISIZE_HIGH_BIT;
ptr = s->physical_start;
header = cast(gbAllocationHeader *)ptr;
data = gb_align_forward(header+1, alignment);
ptr = gb_pointer_add(ptr, size);
}
if (!gb_scratch_memory_is_in_use(s, ptr)) {
gb_allocation_header_fill(header, ptr, gb_pointer_diff(header, ptr));
s->alloc_point = cast(u8 *)ptr;
return data;
}
} break;
case GB_ALLOCATION_FREE: {
if (old_memory) {
void *end = gb_pointer_add(s->physical_start, s->total_size);
if (old_memory < s->physical_start || old_memory >= end) {
GB_ASSERT(false);
} else {
/* NOTE(bill): Mark as free */
gbAllocationHeader *h = gb_allocation_header(old_memory);
GB_ASSERT((h->size & GB_ISIZE_HIGH_BIT) == 0);
h->size = h->size | GB_ISIZE_HIGH_BIT;
while (s->free_point != s->alloc_point) {
gbAllocationHeader *header = cast(gbAllocationHeader *)s->free_point;
if ((header->size & GB_ISIZE_HIGH_BIT) == 0)
break;
s->free_point = gb_pointer_add(s->free_point, h->size & (~GB_ISIZE_HIGH_BIT));
if (s->free_point == end)
s->free_point = s->physical_start;
}
}
}
} break;
case GB_ALLOCATION_FREE_ALL: {
s->alloc_point = s->physical_start;
s->free_point = s->physical_start;
} break;
case GB_ALLOCATION_RESIZE:
return gb_default_resize_align(gb_scratch_allocator(s), old_memory, old_size, size, alignment);
}
return NULL;
}
@ -5399,6 +5506,8 @@ gb_get_working_cmd(char *buffer, isize len)
#endif
}
gb_inline u16
gb_endian_swap16(u16 i)
{