Splitting things up and adding stuff to the libs

This commit is contained in:
Joseph Ferano 2023-10-25 19:35:13 +07:00
parent dc6365257d
commit ad815adc9f
8 changed files with 720 additions and 109 deletions

View File

@ -10,9 +10,9 @@ RM=rm -vf
build: clean build: clean
$(CC) $(CFLAGS) $(LDLIBS) $(INCLUDES) $(OBJECTS) $(P).c -o $(P) $(CC) $(CFLAGS) $(LDLIBS) $(INCLUDES) $(OBJECTS) $(P).c -o $(P)
.PHONY: texturepacker .PHONY: texpack
texturepacker: texpack:
$(CC) $(CFLAGS) -lSDL2 -lm -lcyaml -L objs/ $(INCLUDES) tools/texturepacker.c -o texturepacker $(CC) $(CFLAGS) -lSDL2 -lm -lcyaml -L objs/ $(INCLUDES) tools/texturepacker.c -o texpack
.PHONY: run .PHONY: run
run: build run: build

132
lib.h
View File

@ -2,6 +2,8 @@
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#define ARENA_IMPLEMENTATION
#include "libs/arena/arena.h"
typedef uint8_t u8; typedef uint8_t u8;
// typedef char16_t c16; // typedef char16_t c16;
@ -25,115 +27,33 @@ typedef struct Animation {
u16 cols; u16 cols;
u16 width; u16 width;
u16 height; u16 height;
} SpriteSheet; } Animation;
void checkCode(int code, char* errorMsg) { typedef struct s8 {
if (code < 0) { u8* data;
fprintf(stderr, "Application Error %i: %s\n", code, errorMsg); usize size;
exit(1); } s8;
}
typedef struct s8_buf {
char* data;
usize size;
usize capcity;
Arena* arena;
} str_buf;
// TODO: It would be cool to use abstract allocators
s8 cstr(char *cstr, Arena *arena) {
int size = strlen(cstr);
void *data = arena_alloc(arena, size);
memcpy(data, cstr, size);
return (s8) { (u8*)data, size };
} }
void* checkPtr(void *ptr, char* errorMsg) { void print_str(s8 string, bool print_nl) {
if (ptr == NULL) { for (usize i = 0; i < string.size; i++) {
fprintf(stderr, "Application Error: %s\n", errorMsg); printf("%c", string.data[i]);
exit(1);
} }
return ptr; if (print_nl) {
} printf("\n");
void checkShader(unsigned int shader, int statusFlag, char* actionName) {
int success;
glGetShaderiv(shader, statusFlag, &success);
if (success < 0) {
fprintf(stderr, "%s Error %i\n", actionName, success);
exit(1);
} }
} }
char* loadText(char* path) {
char* buffer = NULL;
long length;
FILE* f = fopen(path, "rb");
if (f) {
fseek(f, 0, SEEK_END);
length = ftell(f);
fseek(f, 0, SEEK_SET);
buffer = calloc(length, sizeof(char));
if (buffer) {
fread(buffer, 1, length, f);
}
fclose(f);
}
return buffer;
}
void checkCompileErrors(unsigned int object, char* type)
{
int success;
char infoLog[1024];
if (strcmp("PROGRAM", type))
{
glGetShaderiv(object, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(object, 1024, NULL, infoLog);
fprintf(stderr, "| ERROR::SHADER: Compile-time error: Type: %s \n %s\n", type, infoLog);
}
}
else
{
glGetProgramiv(object, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(object, 1024, NULL, infoLog);
fprintf(stderr, "| ERROR::Shader: Link-time error: Type: %s \n %s\n", type, infoLog);
}
}
}
unsigned int compileShaderProgram(char* vertSrcPath, char* fragSrcPath, char* geoSrcPath) {
unsigned int vertShader, fragShader, geoShader;
char* vertSrc = loadText(vertSrcPath);
vertShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertShader, 1, (const GLchar* const*)&vertSrc, NULL);
glCompileShader(vertShader);
checkCompileErrors(vertShader, "VERTEX");
char* fragSrc = loadText(fragSrcPath);
fragShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragShader, 1, (const GLchar* const*)&fragSrc, NULL);
glCompileShader(fragShader);
checkCompileErrors(fragShader, "FRAGMENT");
char* geoSrc = NULL;
if (geoSrcPath != NULL) {
geoSrc = loadText(geoSrcPath);
geoShader = glCreateShader(GL_GEOMETRY_SHADER);
glShaderSource(geoShader, 1, (const GLchar* const*)&geoSrc, NULL);
glCompileShader(geoShader);
checkCompileErrors(geoShader, "GEOMETRY");
}
unsigned int program = glCreateProgram();
glAttachShader(program, vertShader);
glAttachShader(program, fragShader);
if (geoSrcPath != NULL) {
glAttachShader(program, geoShader);
}
glLinkProgram(program);
checkCompileErrors(program, "PROGRAM");
glDeleteShader(vertShader);
glDeleteShader(fragShader);
free(vertSrc);
free(fragSrc);
if (geoSrc != NULL) {
free(geoSrc);
}
return program;
}

118
libs/arena.h Normal file
View File

@ -0,0 +1,118 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
bool is_power_of_two(uintptr_t x) {
return (x & (x-1)) == 0;
}
uintptr_t align_forward(uintptr_t ptr, size_t align) {
uintptr_t p, a, modulo;
assert(is_power_of_two(align));
p = ptr;
a = (uintptr_t)align;
// Same as (p % a) but faster as 'a' is a power of two
modulo = p & (a-1);
if (modulo != 0) {
// If 'p' address is not aligned, push the address to the
// next value which is aligned
p += a - modulo;
}
return p;
}
#ifndef DEFAULT_ALIGNMENT
#define DEFAULT_ALIGNMENT (2*sizeof(void *))
#endif
typedef struct Arena Arena;
struct Arena {
unsigned char *buf;
size_t buf_len;
size_t prev_offset; // This will be useful for later on
size_t curr_offset;
};
void arena_init(Arena *a, void *backing_buffer, size_t backing_buffer_length) {
a->buf = (unsigned char *)backing_buffer;
a->buf_len = backing_buffer_length;
a->curr_offset = 0;
a->prev_offset = 0;
}
void *arena_alloc_align(Arena *a, size_t size, size_t align) {
// Align 'curr_offset' forward to the specified alignment
uintptr_t curr_ptr = (uintptr_t)a->buf + (uintptr_t)a->curr_offset;
uintptr_t offset = align_forward(curr_ptr, align);
offset -= (uintptr_t)a->buf; // Change to relative offset
// Check to see if the backing memory has space left
if (offset+size <= a->buf_len) {
void *ptr = &a->buf[offset];
a->prev_offset = offset;
a->curr_offset = offset+size;
// Zero new memory by default
memset(ptr, 0, size);
return ptr;
}
// Return NULL if the arena is out of memory (or handle differently)
return NULL;
}
// Because C doesn't have default parameters
void *arena_alloc(Arena *a, size_t size) {
return arena_alloc_align(a, size, DEFAULT_ALIGNMENT);
}
// void arena_free(Arena *a, void *ptr) {
// // Do nothing
// }
void *arena_resize_align(Arena *a, void *old_memory, size_t old_size, size_t new_size, size_t align) {
unsigned char *old_mem = (unsigned char *)old_memory;
assert(is_power_of_two(align));
if (old_mem == NULL || old_size == 0) {
return arena_alloc_align(a, new_size, align);
} else if (a->buf <= old_mem && old_mem < a->buf+a->buf_len) {
if (a->buf+a->prev_offset == old_mem) {
a->curr_offset = a->prev_offset + new_size;
if (new_size > old_size) {
// Zero the new memory by default
memset(&a->buf[a->curr_offset], 0, new_size-old_size);
}
return old_memory;
} else {
void *new_memory = arena_alloc_align(a, new_size, align);
size_t copy_size = old_size < new_size ? old_size : new_size;
// Copy across old memory to the new memory
memmove(new_memory, old_memory, copy_size);
return new_memory;
}
} else {
assert(0 && "Memory is out of bounds of the buffer in this arena");
return NULL;
}
}
// Because C doesn't have default parameters
void *arena_resize(Arena *a, void *old_memory, size_t old_size, size_t new_size) {
return arena_resize_align(a, old_memory, old_size, new_size, DEFAULT_ALIGNMENT);
}
void arena_free_all(Arena *a) {
a->curr_offset = 0;
a->prev_offset = 0;
}

20
libs/arena/LICENSE Normal file
View File

@ -0,0 +1,20 @@
Copyright 2022 Alexey Kutepov <reximkut@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

47
libs/arena/README.md Normal file
View File

@ -0,0 +1,47 @@
# Arena Allocator
[Arena Allocator](https://en.wikipedia.org/wiki/Region-based_memory_management) implementation in pure C as an [stb-style single-file library](https://github.com/nothings/stb).
*I just caught myself implementing this over and over again in my projects, so I decided to turn it into a copy-pastable library similar to [sv](http://github.com/tsoding/sv)*
## Quick Start
> The truly reusable code is the one that you can simply copy-paste.
The library itself does not require any special building. You can simple copy-paste [./arena.h](./arena.h) to your project and `#include` it.
```c
#define ARENA_IMPLEMENTATION
#include "arena.h"
static Arena default_arena = {0};
static Arena temporary_arena = {0};
static Arena *context_arena = &default_arena;
void *context_alloc(size_t size)
{
assert(context_arena);
return arena_alloc(context_arena, size);
}
int main(void)
{
// Allocate stuff in default_arena
context_alloc(64);
context_alloc(128);
context_alloc(256);
context_alloc(512);
// Allocate stuff in temporary_arena;
context_arena = &temporary_arena;
context_alloc(64);
context_alloc(128);
context_alloc(256);
context_alloc(512);
// Deallocate everything at once
arena_free(&default_arena);
arena_free(&temporary_arena);
return 0;
}
```

217
libs/arena/arena.h Normal file
View File

@ -0,0 +1,217 @@
// Copyright 2022 Alexey Kutepov <reximkut@gmail.com>
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef ARENA_H_
#define ARENA_H_
#include <stddef.h>
#include <stdint.h>
#ifndef ARENA_ASSERT
#include <assert.h>
#define ARENA_ASSERT assert
#endif
#define ARENA_BACKEND_LIBC_MALLOC 0
#define ARENA_BACKEND_LINUX_MMAP 1
#define ARENA_BACKEND_WIN32_VIRTUALALLOC 2
#define ARENA_BACKEND_WASM_HEAPBASE 3
#ifndef ARENA_BACKEND
#define ARENA_BACKEND ARENA_BACKEND_LIBC_MALLOC
#endif // ARENA_BACKEND
typedef struct Region Region;
struct Region {
Region *next;
size_t count;
size_t capacity;
uintptr_t data[];
};
typedef struct {
Region *begin, *end;
} Arena;
#define REGION_DEFAULT_CAPACITY (8*1024)
Region *new_region(size_t capacity);
void free_region(Region *r);
// TODO: snapshot/rewind capability for the arena
// - Snapshot should be combination of a->end and a->end->count.
// - Rewinding should be restoring a->end and a->end->count from the snapshot and
// setting count-s of all the Region-s after the remembered a->end to 0.
void *arena_alloc(Arena *a, size_t size_bytes);
void *arena_realloc(Arena *a, void *oldptr, size_t oldsz, size_t newsz);
void arena_reset(Arena *a);
void arena_free(Arena *a);
#endif // ARENA_H_
#ifdef ARENA_IMPLEMENTATION
#if ARENA_BACKEND == ARENA_BACKEND_LIBC_MALLOC
#include <stdlib.h>
// TODO: instead of accepting specific capacity new_region() should accept the size of the object we want to fit into the region
// It should be up to new_region() to decide the actual capacity to allocate
Region *new_region(size_t capacity)
{
size_t size_bytes = sizeof(Region) + sizeof(uintptr_t)*capacity;
// TODO: it would be nice if we could guarantee that the regions are allocated by ARENA_BACKEND_LIBC_MALLOC are page aligned
Region *r = malloc(size_bytes);
ARENA_ASSERT(r);
r->next = NULL;
r->count = 0;
r->capacity = capacity;
return r;
}
void free_region(Region *r)
{
free(r);
}
#elif ARENA_BACKEND == ARENA_BACKEND_LINUX_MMAP
# error "TODO: Linux mmap backend is not implemented yet"
#elif ARENA_BACKEND == ARENA_BACKEND_WIN32_VIRTUALALLOC
#if !defined(_WIN32)
# error "Current platform is not Windows"
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define INV_HANDLE(x) (((x) == NULL) || ((x) == INVALID_HANDLE_VALUE))
Region *new_region(size_t capacity)
{
SIZE_T size_bytes = sizeof(Region) + sizeof(uintptr_t) * capacity;
Region *r = VirtualAllocEx(
GetCurrentProcess(), /* Allocate in current process address space */
NULL, /* Unknown position */
size_bytes, /* Bytes to allocate */
MEM_COMMIT | MEM_RESERVE, /* Reserve and commit allocated page */
PAGE_READWRITE /* Permissions ( Read/Write )*/
);
if (INV_HANDLE(r))
ARENA_ASSERT(0 && "VirtualAllocEx() failed.");
r->next = NULL;
r->count = 0;
r->capacity = capacity;
return r;
}
void free_region(Region *r)
{
if (INV_HANDLE(r))
return;
BOOL free_result = VirtualFreeEx(
GetCurrentProcess(), /* Deallocate from current process address space */
(LPVOID)r, /* Address to deallocate */
0, /* Bytes to deallocate ( Unknown, deallocate entire page ) */
MEM_RELEASE /* Release the page ( And implicitly decommit it ) */
);
if (FALSE == free_result)
ARENA_ASSERT(0 && "VirtualFreeEx() failed.");
}
#elif ARENA_BACKEND == ARENA_BACKEND_WASM_HEAPBASE
# error "TODO: WASM __heap_base backend is not implemented yet"
#else
# error "Unknown Arena backend"
#endif
// TODO: add debug statistic collection mode for arena
// Should collect things like:
// - How many times new_region was called
// - How many times existing region was skipped
// - How many times allocation exceeded REGION_DEFAULT_CAPACITY
void *arena_alloc(Arena *a, size_t size_bytes)
{
size_t size = (size_bytes + sizeof(uintptr_t) - 1)/sizeof(uintptr_t);
if (a->end == NULL) {
ARENA_ASSERT(a->begin == NULL);
size_t capacity = REGION_DEFAULT_CAPACITY;
if (capacity < size) capacity = size;
a->end = new_region(capacity);
a->begin = a->end;
}
while (a->end->count + size > a->end->capacity && a->end->next != NULL) {
a->end = a->end->next;
}
if (a->end->count + size > a->end->capacity) {
ARENA_ASSERT(a->end->next == NULL);
size_t capacity = REGION_DEFAULT_CAPACITY;
if (capacity < size) capacity = size;
a->end->next = new_region(capacity);
a->end = a->end->next;
}
void *result = &a->end->data[a->end->count];
a->end->count += size;
return result;
}
void *arena_realloc(Arena *a, void *oldptr, size_t oldsz, size_t newsz)
{
if (newsz <= oldsz) return oldptr;
void *newptr = arena_alloc(a, newsz);
char *newptr_char = newptr;
char *oldptr_char = oldptr;
for (size_t i = 0; i < oldsz; ++i) {
newptr_char[i] = oldptr_char[i];
}
return newptr;
}
void arena_reset(Arena *a)
{
for (Region *r = a->begin; r != NULL; r = r->next) {
r->count = 0;
}
a->end = a->begin;
}
void arena_free(Arena *a)
{
Region *r = a->begin;
while (r) {
Region *r0 = r;
r = r->next;
free_region(r0);
}
a->begin = NULL;
a->end = NULL;
}
#endif // ARENA_IMPLEMENTATION

113
rendering.h Normal file
View File

@ -0,0 +1,113 @@
#pragma once
#include <GLFW/glfw3.h>
void checkCode(int code, char* errorMsg) {
if (code < 0) {
fprintf(stderr, "Application Error %i: %s\n", code, errorMsg);
exit(1);
}
}
void* checkPtr(void *ptr, char* errorMsg) {
if (ptr == NULL) {
fprintf(stderr, "Application Error: %s\n", errorMsg);
exit(1);
}
return ptr;
}
void checkShader(unsigned int shader, int statusFlag, char* actionName) {
int success;
glGetShaderiv(shader, statusFlag, &success);
if (success < 0) {
fprintf(stderr, "%s Error %i\n", actionName, success);
exit(1);
}
}
char* loadText(char* path) {
char* buffer = NULL;
long length;
FILE* f = fopen(path, "rb");
if (f) {
fseek(f, 0, SEEK_END);
length = ftell(f);
fseek(f, 0, SEEK_SET);
buffer = calloc(length, sizeof(char));
if (buffer) {
fread(buffer, 1, length, f);
}
fclose(f);
}
return buffer;
}
void checkCompileErrors(unsigned int object, char* type)
{
int success;
char infoLog[1024];
if (strcmp("PROGRAM", type))
{
glGetShaderiv(object, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(object, 1024, NULL, infoLog);
fprintf(stderr, "| ERROR::SHADER: Compile-time error: Type: %s \n %s\n", type, infoLog);
}
}
else
{
glGetProgramiv(object, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(object, 1024, NULL, infoLog);
fprintf(stderr, "| ERROR::Shader: Link-time error: Type: %s \n %s\n", type, infoLog);
}
}
}
unsigned int compileShaderProgram(char* vertSrcPath, char* fragSrcPath, char* geoSrcPath) {
unsigned int vertShader, fragShader, geoShader;
char* vertSrc = loadText(vertSrcPath);
vertShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertShader, 1, (const GLchar* const*)&vertSrc, NULL);
glCompileShader(vertShader);
checkCompileErrors(vertShader, "VERTEX");
char* fragSrc = loadText(fragSrcPath);
fragShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragShader, 1, (const GLchar* const*)&fragSrc, NULL);
glCompileShader(fragShader);
checkCompileErrors(fragShader, "FRAGMENT");
char* geoSrc = NULL;
if (geoSrcPath != NULL) {
geoSrc = loadText(geoSrcPath);
geoShader = glCreateShader(GL_GEOMETRY_SHADER);
glShaderSource(geoShader, 1, (const GLchar* const*)&geoSrc, NULL);
glCompileShader(geoShader);
checkCompileErrors(geoShader, "GEOMETRY");
}
unsigned int program = glCreateProgram();
glAttachShader(program, vertShader);
glAttachShader(program, fragShader);
if (geoSrcPath != NULL) {
glAttachShader(program, geoShader);
}
glLinkProgram(program);
checkCompileErrors(program, "PROGRAM");
glDeleteShader(vertShader);
glDeleteShader(fragShader);
free(vertSrc);
free(fragSrc);
if (geoSrc != NULL) {
free(geoSrc);
}
return program;
}

176
stack.h Normal file
View File

@ -0,0 +1,176 @@
#pragma once
#ifndef DEFAULT_ALIGNMENT
#define DEFAULT_ALIGNMENT (2*sizeof(void *))
#endif
bool is_power_of_two(uintptr_t x) {
return (x & (x-1)) == 0;
}
typedef struct Stack {
unsigned char *buf;
size_t buf_len;
size_t prev_offset;
size_t curr_offset;
} Stack;
typedef struct Stack_Allocation_Header {
size_t prev_offset;
size_t padding;
} Stack_Allocation_Header;
void stack_init(Stack *s, void *backing_buffer, size_t backing_buffer_length) {
s->buf = (unsigned char *)backing_buffer;
s->buf_len = backing_buffer_length;
s->prev_offset = 0;
s->curr_offset = 0;
}
size_t calc_padding_with_header(uintptr_t ptr, uintptr_t alignment, size_t header_size) {
uintptr_t p, a, modulo, padding, needed_space;
assert(is_power_of_two(alignment));
p = ptr;
a = alignment;
modulo = p & (a-1); // (p % a) as it assumes alignment is a power of two
padding = 0;
needed_space = 0;
if (modulo != 0) { // Same logic as 'align_forward'
padding = a - modulo;
}
needed_space = (uintptr_t)header_size;
if (padding < needed_space) {
needed_space -= padding;
if ((needed_space & (a-1)) != 0) {
padding += a * (1+(needed_space/a));
} else {
padding += a * (needed_space/a);
}
}
return (size_t)padding;
}
void *stack_alloc_align(Stack *s, size_t size, size_t alignment) {
uintptr_t curr_addr, next_addr;
size_t padding;
Stack_Allocation_Header *header;
assert(is_power_of_two(alignment));
if (alignment > 128) {
// As the padding is 8 bits (1 byte), the largest alignment that can
// be used is 128 bytes
alignment = 128;
}
curr_addr = (uintptr_t)s->buf + (uintptr_t)s->curr_offset;
usize sah_size = sizeof(Stack_Allocation_Header);
padding = calc_padding_with_header(curr_addr, (uintptr_t)alignment, sah_size);
if (s->curr_offset + padding + size > s->buf_len) {
// Stack allocator is out of memory
return NULL;
}
s->prev_offset = s->curr_offset; // Store the previous offset
s->curr_offset += padding;
next_addr = curr_addr + (uintptr_t)padding;
header = (Stack_Allocation_Header *)(next_addr - sizeof(Stack_Allocation_Header));
header->padding = padding;
header->prev_offset = s->prev_offset; // store the previous offset in the header
s->curr_offset += size;
return memset((void *)next_addr, 0, size);
}
// Because C does not have default parameters
void *stack_alloc(Stack *s, size_t size) {
return stack_alloc_align(s, size, DEFAULT_ALIGNMENT);
}
void stack_free(Stack *s, void *ptr) {
if (ptr != NULL) {
uintptr_t start, end, curr_addr;
Stack_Allocation_Header *header;
size_t prev_offset;
start = (uintptr_t)s->buf;
end = start + (uintptr_t)s->buf_len;
curr_addr = (uintptr_t)ptr;
if (!(start <= curr_addr && curr_addr < end)) {
assert(0 && "Out of bounds memory address passed to stack allocator (free)");
return;
}
if curr_addr >= start+(uintptr_t)s->offset {
// Allow double frees
return;
}
header = (Stack_Allocation_Header *)(curr_addr - sizeof(Stack_Allocation_Header));
// Calculate previous offset from the header and its address
prev_offset = (size_t)(curr_addr - (uintptr_t)header->padding - start);
if (prev_offset != header->prev_offset) {
assert(0 && "Out of order stack allocator free");
return;
}
// Reset the offsets to the previous allocation
s->curr_offset = s->prev_offset;
s->prev_offset = header->prev_offset;
}
}
void *stack_resize_align(Stack *s, void *ptr, size_t old_size, size_t new_size, size_t alignment) {
if (ptr == NULL) {
return stack_alloc_align(s, new_size, alignment);
} else if (new_size == 0) {
stack_free(s, ptr);
return NULL;
} else {
uintptr_t start, end, curr_addr;
size_t min_size = old_size < new_size ? old_size : new_size;
void *new_ptr;
start = (uintptr_t)s->buf;
end = start + (uintptr_t)s->buf_len;
curr_addr = (uintptr_t)ptr;
if (!(start <= curr_addr && curr_addr < end)) {
assert(0 && "Out of bounds memory address passed to stack allocator (resize)");
return NULL;
}
if (curr_addr >= start + (uintptr_t)s->offset) {
// Treat as a double free
return NULL;
}
if old_size == size {
return ptr;
}
new_ptr = stack_alloc_align(s, new_size, alignment);
memmove(new_ptr, ptr, min_size);
return new_ptr;
}
}
void *stack_resize(Stack *s, void *ptr, size_t old_size, size_t new_size) {
return stack_resize_align(s, ptr, old_size, new_size, DEFAULT_ALIGNMENT);
}
void stack_free_all(Stack *s) {
s->offset = 0;
}