Explicit allocators

This commit is contained in:
Charlie 2024-07-01 13:10:06 +02:00
parent bf32265517
commit ea37ceb3bd
12 changed files with 172 additions and 221 deletions

View file

@ -18,9 +18,9 @@ int start(int argc, char **argv) {
window.clear_color = hex_to_rgba(0x2a2d3aff);
Gfx_Image *bush_image = load_image_from_disk(fixed_string("berry_bush.png"));
Gfx_Image *bush_image = load_image_from_disk(fixed_string("berry_bush.png"), get_heap_allocator());
assert(bush_image, "Failed loading berry_bush.png");
Gfx_Image *hammer_image = load_image_from_disk(fixed_string("hammer.png"));
Gfx_Image *hammer_image = load_image_from_disk(fixed_string("hammer.png"), get_heap_allocator());
assert(hammer_image, "Failed loading hammer.png");
seed_for_random = os_get_current_cycle_count();
@ -33,7 +33,6 @@ int start(int argc, char **argv) {
float64 last_time = os_get_current_time_in_seconds();
while (!window.should_close) {
reset_temporary_storage();
context.allocator = temp;
float64 now = os_get_current_time_in_seconds();
float64 delta = now - last_time;
@ -58,11 +57,9 @@ int start(int argc, char **argv) {
}
if (is_key_just_released('Q')) {
push_allocator(get_heap_allocator());
delete_image(bush_image);
bush_image = load_image_from_disk(fixed_string("berry_bush.png"));
bush_image = load_image_from_disk(fixed_string("berry_bush.png"), get_heap_allocator());
assert(bush_image, "Failed loading berry_bush.png");
pop_allocator();
}
const float32 cam_move_speed = 4.0;

View file

@ -101,11 +101,12 @@ void printf(const char* fmt, ...);
#define FIRST_ARG(arg1, ...) arg1
#define SECOND_ARG(arg1, arg2, ...) arg2
#define print(...) _Generic((FIRST_ARG(__VA_ARGS__)), \
string: prints, \
default: printf \
)(__VA_ARGS__)
#define sprint(...) _Generic((FIRST_ARG(__VA_ARGS__)), \
#define sprint(...) _Generic((SECOND_ARG(__VA_ARGS__)), \
string: sprints, \
default: sprintf \
)(__VA_ARGS__)
@ -125,7 +126,7 @@ typedef enum Allocator_Message {
ALLOCATOR_DEALLOCATE,
ALLOCATOR_REALLOCATE,
} Allocator_Message;
typedef void*(*Allocator_Proc)(u64, void*, Allocator_Message);
typedef void*(*Allocator_Proc)(u64, void*, Allocator_Message, void*);
typedef enum Log_Level {
LOG_ERROR,
@ -142,7 +143,6 @@ typedef struct Allocator {
} Allocator;
typedef struct Context {
Allocator allocator;
void *logger; // void(*Logger_Proc)(Log_Level level, string fmt, ...)
CONTEXT_EXTRA extra;
@ -154,8 +154,14 @@ thread_local Context context;
thread_local Context context_stack[CONTEXT_STACK_MAX];
thread_local u64 num_contexts = 0;
void* alloc(u64 size) { return context.allocator.proc(size, NULL, ALLOCATOR_ALLOCATE); }
void dealloc(void *p) { context.allocator.proc(0, p, ALLOCATOR_DEALLOCATE); }
forward_global thread_local Allocator temp;
void* alloc(Allocator allocator, u64 size) {
return allocator.proc(size, 0, ALLOCATOR_ALLOCATE, allocator.data);
}
void dealloc(Allocator allocator, void *p) {
allocator.proc(0, p, ALLOCATOR_DEALLOCATE, allocator.data);
}
void push_context(Context c) {
assert(num_contexts < CONTEXT_STACK_MAX, "Context stack overflow");
@ -170,11 +176,4 @@ void pop_context() {
context = context_stack[num_contexts];
}
void push_allocator(Allocator a) {
Context c = context;
c.allocator = a;
push_context(c);
}
void pop_allocator() { pop_context(); }

View file

@ -185,16 +185,17 @@ Draw_Quad *draw_image_xform(Gfx_Image *image, Matrix4 xform, Vector2 size, Vecto
#define COLOR_WHITE ((Vector4){1.0, 1.0, 1.0, 1.0})
#define COLOR_BLACK ((Vector4){0.0, 0.0, 0.0, 1.0})
// context.allocator
Gfx_Image *load_image_from_disk(string path) {
Gfx_Image *load_image_from_disk(string path, Allocator allocator) {
string png;
bool ok = os_read_entire_file(path, &png);
bool ok = os_read_entire_file(path, &png, allocator);
if (!ok) return 0;
Gfx_Image *image = alloc(sizeof(Gfx_Image));
Gfx_Image *image = alloc(allocator, sizeof(Gfx_Image));
// This is fucking terrible I gotta write my own decoder
lodepng_allocator = allocator;
LodePNGState state;
lodepng_state_init(&state);
u32 error = lodepng_inspect(&image->width, &image->height, &state, png.data, png.count);
@ -214,7 +215,7 @@ Gfx_Image *load_image_from_disk(string path) {
lodepng_state_cleanup(&state);
dealloc_string(png);
dealloc_string(allocator, png);
if (error) {
return 0;
@ -222,7 +223,7 @@ Gfx_Image *load_image_from_disk(string path) {
// We need to flip the image
u32 row_bytes = image->width * 4; // #Magicvalue assuming 4 bytes
u8* temp_row = (u8*)alloc(row_bytes);
u8* temp_row = (u8*)alloc(temp, row_bytes);
for (u32 i = 0; i < image->height / 2; i++) {
u8* top_row = image->data + i * row_bytes;
u8* bottom_row = image->data + (image->height - i - 1) * row_bytes;
@ -232,17 +233,18 @@ Gfx_Image *load_image_from_disk(string path) {
memcpy(top_row, bottom_row, row_bytes);
memcpy(bottom_row, temp_row, row_bytes);
}
dealloc(temp_row);
image->gfx_handle = GFX_INVALID_HANDLE; // This is handled in gfx
image->allocator = allocator;
return image;
}
void delete_image(Gfx_Image *image) {
dealloc(image->data);
dealloc(image->allocator, image->data);
image->width = 0;
image->height = 0;
draw_frame.garbage_stack[draw_frame.garbage_stack_count] = image->gfx_handle;
draw_frame.garbage_stack_count += 1;
dealloc(image);
dealloc(image->allocator, image);
}

View file

@ -313,7 +313,7 @@ void gfx_init() {
#if OOGABOOGA_DEV
string source;
bool source_ok = os_read_entire_file(fxstr("oogabooga/dev/d3d11_image_shader.hlsl"), &source);
bool source_ok = os_read_entire_file(fxstr("oogabooga/dev/d3d11_image_shader.hlsl"), &source, get_heap_allocator()); // #Leak
assert(source_ok, "Could not open d3d11_image_shader source");
// Compile vertex shader

View file

@ -33,5 +33,6 @@ typedef struct Gfx_Image {
u32 width, height;
u8 *data;
Gfx_Handle gfx_handle;
Allocator allocator;
} Gfx_Image;

View file

@ -12,7 +12,7 @@ u64 program_memory_size = 0;
u8 init_memory_arena[INIT_MEMORY_SIZE];
u8 *init_memory_head = init_memory_arena;
void* initialization_allocator_proc(u64 size, void *p, Allocator_Message message) {
void* initialization_allocator_proc(u64 size, void *p, Allocator_Message message, void *data) {
switch (message) {
case ALLOCATOR_ALLOCATE: {
p = init_memory_head;
@ -35,6 +35,12 @@ void* initialization_allocator_proc(u64 size, void *p, Allocator_Message message
return 0;
}
Allocator get_initialization_allocator() {
Allocator a;
a.proc = initialization_allocator_proc;
return a;
}
///
///
// Basic general heap allocator, free list
@ -206,7 +212,7 @@ void heap_init() {
if (heap_initted) return;
heap_initted = true;
heap_head = make_heap_block(0, DEFAULT_HEAP_BLOCK_SIZE);
heap_lock = os_make_spinlock();
heap_lock = os_make_spinlock(get_initialization_allocator());
}
void *heap_alloc(u64 size) {
@ -376,7 +382,7 @@ void heap_dealloc(void *p) {
os_spinlock_unlock(heap_lock);
}
void* heap_allocator_proc(u64 size, void *p, Allocator_Message message) {
void* heap_allocator_proc(u64 size, void *p, Allocator_Message message, void* data) {
switch (message) {
case ALLOCATOR_ALLOCATE: {
return heap_alloc(size);
@ -421,7 +427,7 @@ Allocator get_heap_allocator() {
#endif
void* talloc(u64);
void* temp_allocator_proc(u64 size, void *p, Allocator_Message message);
void* temp_allocator_proc(u64 size, void *p, Allocator_Message message, void*);
thread_local void * temporary_storage = 0;
thread_local bool temporary_storage_initted = false;
@ -430,7 +436,7 @@ thread_local bool has_warned_temporary_storage_overflow = false;
thread_local Allocator temp;
void* temp_allocator_proc(u64 size, void *p, Allocator_Message message) {
void* temp_allocator_proc(u64 size, void *p, Allocator_Message message, void* data) {
switch (message) {
case ALLOCATOR_ALLOCATE: {
return talloc(size);
@ -457,6 +463,8 @@ void temporary_storage_init() {
temp.data = 0;
temporary_storage_initted = true;
temp.proc = temp_allocator_proc;
}
void* talloc(u64 size) {
@ -487,8 +495,3 @@ void reset_temporary_storage() {
has_warned_temporary_storage_overflow = true;
}
// So we can do this in code included before this.
void push_temp_allocator() {
if (!temporary_storage_initted) temporary_storage_init();
push_allocator(temp);
}

View file

@ -32,16 +32,22 @@
#include <math.h>
Allocator get_heap_allocator();
// Custom allocators for lodepng
Allocator lodepng_allocator = {0};
void* lodepng_malloc(size_t size) {
return context.allocator.proc((u64)size, 0, ALLOCATOR_ALLOCATE);
if (lodepng_allocator.proc) return alloc(lodepng_allocator, size);
return alloc(get_heap_allocator(), size);
}
void* lodepng_realloc(void* ptr, size_t new_size) {
return context.allocator.proc((u64)new_size, ptr, ALLOCATOR_REALLOCATE);
if (lodepng_allocator.proc) return lodepng_allocator.proc(new_size, ptr, ALLOCATOR_REALLOCATE, lodepng_allocator.data);
return get_heap_allocator().proc(new_size, ptr, ALLOCATOR_REALLOCATE, get_heap_allocator().data);
}
void lodepng_free(void* ptr) {
if (!ptr) return;
context.allocator.proc(0, ptr, ALLOCATOR_DEALLOCATE);
if (lodepng_allocator.proc) dealloc(lodepng_allocator, ptr);
else dealloc(get_heap_allocator(), ptr);
}
#define LODEPNG_NO_COMPILE_ALLOCATORS
//#define LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
@ -124,7 +130,7 @@ void lodepng_free(void* ptr) {
#undef near
#endif
#ifdef far
#undef far
#undef far
#endif
#include "random.c"
@ -161,10 +167,11 @@ void lodepng_free(void* ptr) {
void oogabooga_init(u64 program_memory_size) {
context.logger = default_logger;
temp = get_initialization_allocator();
os_init(program_memory_size);
gfx_init();
heap_init();
temporary_storage_init();
gfx_init();
}
#ifndef INITIAL_PROGRAM_MEMORY_SIZE
@ -178,7 +185,6 @@ void oogabooga_init(u64 program_memory_size) {
int ENTRY_PROC(int argc, char **argv);
int main(int argc, char **argv) {
context.allocator.proc = initialization_allocator_proc;
oogabooga_init(INITIAL_PROGRAM_MEMORY_SIZE);
printf("Ooga booga program started\n");

View file

@ -162,7 +162,6 @@ void os_init(u64 program_memory_size) {
heap_init();
context.allocator = get_heap_allocator();
os.crt = os_load_dynamic_library(const_string("msvcrt.dll"));
assert(os.crt != 0, "Could not load win32 crt library. Might be compiled with non-msvc? #Incomplete #Portability");
@ -308,8 +307,8 @@ DWORD WINAPI win32_thread_invoker(LPVOID param) {
return 0;
}
Thread* os_make_thread(Thread_Proc proc) {
Thread *t = (Thread*)alloc(sizeof(Thread));
Thread* os_make_thread(Thread_Proc proc, Allocator allocator) {
Thread *t = (Thread*)alloc(allocator, sizeof(Thread));
t->id = 0; // This is set when we start it
t->proc = proc;
t->initial_context = context;
@ -365,8 +364,9 @@ void os_unlock_mutex(Mutex_Handle m) {
///
// Spinlock "primitive"
Spinlock *os_make_spinlock() {
Spinlock *l = cast(Spinlock*)alloc(sizeof(Spinlock));
Spinlock *os_make_spinlock(Allocator allocator) {
// #Memory #Cleanup do we need to heap allocate this ?
Spinlock *l = cast(Spinlock*)alloc(allocator, sizeof(Spinlock));
l->locked = false;
return l;
}
@ -490,15 +490,14 @@ void os_write_string_to_stdout(string s) {
WriteFile(win32_stdout, s.data, s.count, 0, NULL);
}
// context.allocator
u16 *win32_fixed_utf8_to_null_terminated_wide(string utf8) {
u16 *win32_fixed_utf8_to_null_terminated_wide(string utf8, Allocator allocator) {
u64 utf16_length = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)utf8.data, (int)utf8.count, 0, 0);
u16 *utf16_str = (u16 *)alloc((utf16_length + 1) * sizeof(u16));
u16 *utf16_str = (u16 *)alloc(allocator, (utf16_length + 1) * sizeof(u16));
int result = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)utf8.data, (int)utf8.count, utf16_str, utf16_length);
if (result == 0) {
dealloc(utf16_str);
dealloc(allocator, utf16_str);
return NULL;
}
@ -507,19 +506,16 @@ u16 *win32_fixed_utf8_to_null_terminated_wide(string utf8) {
return utf16_str;
}
u16 *temp_win32_fixed_utf8_to_null_terminated_wide(string utf8) {
push_temp_allocator();
u16 *result = win32_fixed_utf8_to_null_terminated_wide(utf8);
pop_allocator();
return result;
return win32_fixed_utf8_to_null_terminated_wide(utf8, temp);
}
string win32_null_terminated_wide_to_fixed_utf8(const u16 *utf16) {
string win32_null_terminated_wide_to_fixed_utf8(const u16 *utf16, Allocator allocator) {
u64 utf8_length = WideCharToMultiByte(CP_UTF8, 0, (LPCWCH)utf16, -1, 0, 0, 0, 0);
u8 *utf8_str = (u8 *)alloc(utf8_length * sizeof(u8));
u8 *utf8_str = (u8 *)alloc(allocator, utf8_length * sizeof(u8));
int result = WideCharToMultiByte(CP_UTF8, 0, (LPCWCH)utf16, -1, (LPSTR)utf8_str, (int)utf8_length, 0, 0);
if (result == 0) {
dealloc(utf8_str);
dealloc(allocator, utf8_str);
return (string){0, 0};
}
@ -531,10 +527,7 @@ string win32_null_terminated_wide_to_fixed_utf8(const u16 *utf16) {
}
string temp_win32_null_terminated_wide_to_fixed_utf8(const u16 *utf16) {
push_temp_allocator();
string result = win32_null_terminated_wide_to_fixed_utf8(utf16);
pop_allocator();
return result;
return win32_null_terminated_wide_to_fixed_utf8(utf16, temp);
}
@ -600,19 +593,19 @@ bool os_write_entire_file(string path, string data) {
return result;
}
bool os_read_entire_file_handle(File f, string *result) {
bool os_read_entire_file_handle(File f, string *result, Allocator allocator) {
LARGE_INTEGER file_size;
if (!GetFileSizeEx(f, &file_size)) {
return false;
}
u64 actual_read = 0;
result->data = (u8*)alloc(file_size.QuadPart);
result->data = (u8*)alloc(allocator, file_size.QuadPart);
result->count = file_size.QuadPart;
bool ok = os_file_read(f, result->data, file_size.QuadPart, &actual_read);
if (!ok) {
dealloc(result->data);
dealloc(allocator, result->data);
result->data = 0;
return false;
}
@ -620,12 +613,12 @@ bool os_read_entire_file_handle(File f, string *result) {
return actual_read == file_size.QuadPart;
}
bool os_read_entire_file(string path, string *result) {
bool os_read_entire_file(string path, string *result, Allocator allocator) {
File file = os_file_open(path, O_READ);
if (file == OS_INVALID_FILE) {
return false;
}
bool res = os_read_entire_file_handle(file, result);
bool res = os_read_entire_file_handle(file, result, allocator);
os_file_close(file);
return res;
}

View file

@ -127,7 +127,7 @@ typedef struct Thread {
///
// Thread primitive
Thread* os_make_thread(Thread_Proc proc);
Thread* os_make_thread(Thread_Proc proc, Allocator allocator);
void os_start_thread(Thread* t);
void os_join_thread(Thread* t);
@ -145,7 +145,7 @@ void os_unlock_mutex(Mutex_Handle m);
typedef struct Spinlock {
bool locked;
} Spinlock;
Spinlock *os_make_spinlock();
Spinlock *os_make_spinlock(Allocator allocator);
void os_spinlock_lock(Spinlock* l);
void os_spinlock_unlock(Spinlock* l);
@ -210,8 +210,8 @@ bool os_file_read(File f, void* buffer, u64 bytes_to_read, u64 *actual_read_byte
bool os_write_entire_file_handle(File f, string data);
bool os_write_entire_file(string path, string data);
bool os_read_entire_file_handle(File f, string *result);
bool os_read_entire_file(string path, string *result);
bool os_read_entire_file_handle(File f, string *result, Allocator allocator);
bool os_read_entire_file(string path, string *result, Allocator allocator);
bool os_is_file(string path);
bool os_is_directory(string path);

View file

@ -1,41 +1,6 @@
/*
Usage:
// We need to use macro const_string to convert literal to string
string fmt = const_string("Pointer address: 0x%x");
print(fmt, cast(u64)&a); // Print to stdout
// Format a string and allocate with context.allocator
string a = sprint("Hello, %cs!\n", "balls"); // %cs for char*
string balls = const_string("balls");
// tprint for temporary allocation
string b = tprint("Hello, %s!\n", balls); // %s for string
// Allocate a new string of length 12 (with context allocator)
string c = alloc_string(12);
dealloc_string(c);
// We can use raw char* for format with printf/sprintf/tprintf
printf("Hello, %!\n", balls);
// concatenation
string concatenated = string_concat(a, b);
// Use temporary memory to make a null-terminated copy of fixed-length string
char* cstring = temp_convert_to_null_terminated_string(balls);
// To convert a cstring to string (using same memory)
string s;
s.data = (u8*)cstring;
s.count = strlen(cstring);
// String matching
bool match = strings_match(a, b);
// View into "Hello, balls!\n" from index 7 with a count of 5; "balls"
string balls2 = string_view(a, 7, 5);
*/
@ -48,8 +13,6 @@ typedef struct string {
u8 *data;
} string;
void push_temp_allocator();
// Not sure what to call this lol
#define fxstr fixed_string
#define fixed_string const_string
@ -65,43 +28,37 @@ inline u64 length_of_null_terminated_string(const char* cstring) {
return len;
}
string alloc_string(u64 count) {
string alloc_string(Allocator allocator, u64 count) {
string s;
s.count = count;
s.data = cast(u8*)alloc(count);
s.data = cast(u8*)alloc(allocator, count);
return s;
}
void dealloc_string(string s) {
dealloc(s.data);
void dealloc_string(Allocator allocator, string s) {
dealloc(allocator, s.data);
}
string talloc_string(u64 count) {
push_temp_allocator();
string s = alloc_string(count);
pop_allocator();
string s = alloc_string(temp, count);
return s;
}
// context.allocator !
string string_concat(const string left, const string right) {
string string_concat(const string left, const string right, Allocator allocator) {
string result;
result.count = left.count + right.count;
result.data = cast(u8*)alloc(result.count);
result.data = cast(u8*)alloc(allocator, result.count);
memcpy(result.data, left.data, left.count);
memcpy(result.data+left.count, right.data, right.count);
return result;
}
// context.allocator !
char *convert_to_null_terminated_string(const string s) {
char *cstring = cast(char*)alloc(s.count+1);
char *convert_to_null_terminated_string(const string s, Allocator allocator) {
char *cstring = cast(char*)alloc(allocator, s.count+1);
memcpy(cstring, s.data, s.count);
cstring[s.count] = 0;
return cstring;
}
char *temp_convert_to_null_terminated_string(const string s) {
push_temp_allocator();
char *c = convert_to_null_terminated_string(s);
pop_allocator();
char *c = convert_to_null_terminated_string(s, temp);
return c;
}
bool strings_match(string a, string b) {

View file

@ -102,24 +102,24 @@ string sprint_va_list_to_buffer(const string fmt, va_list args, void* buffer, u6
char* fmt_cstring = temp_convert_to_null_terminated_string(fmt);
return sprint_null_terminated_string_va_list_to_buffer(fmt_cstring, args, buffer, count);
}
// context.allocator
string sprint_va_list(const string fmt, va_list args) {
string sprint_va_list(Allocator allocator, const string fmt, va_list args) {
char* fmt_cstring = temp_convert_to_null_terminated_string(fmt);
u64 count = format_string_to_buffer(NULL, 0, fmt_cstring, args) + 1;
char* buffer = NULL;
buffer = (char*)alloc(count);
buffer = (char*)alloc(allocator, count);
return sprint_null_terminated_string_va_list_to_buffer(fmt_cstring, args, buffer, count);
}
// context.allocator
string sprints(const string fmt, ...) {
string sprints(Allocator allocator, const string fmt, ...) {
va_list args = 0;
va_start(args, fmt);
string s = sprint_va_list(fmt, args);
string s = sprint_va_list(allocator, fmt, args);
va_end(args);
return s;
}
@ -128,22 +128,20 @@ string sprints(const string fmt, ...) {
string tprints(const string fmt, ...) {
va_list args = 0;
va_start(args, fmt);
push_temp_allocator();
string s = sprint_va_list(fmt, args);
pop_allocator();
string s = sprint_va_list(temp, fmt, args);
va_end(args);
return s;
}
// context.allocator
string sprintf(const char *fmt, ...) {
string sprintf(Allocator allocator, const char *fmt, ...) {
string sfmt;
sfmt.data = cast(u8*)fmt;
sfmt.count = strlen(fmt);
va_list args;
va_start(args, fmt);
string s = sprint_va_list(sfmt, args);
string s = sprint_va_list(allocator, sfmt, args);
va_end(args);
return s;
@ -156,22 +154,12 @@ string tprintf(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
push_temp_allocator();
string s = sprint_va_list(sfmt, args);
pop_allocator();
string s = sprint_va_list(temp, sfmt, args);
va_end(args);
return s;
}
// context.allocator (alloc & dealloc)
void print_va_list(const string fmt, va_list args) {
string s = sprint_va_list(fmt, args);
os_write_string_to_stdout(s);
dealloc(s.data);
}
// print for 'string' and printf for 'char*'
#define PRINT_BUFFER_SIZE 4096
@ -203,14 +191,14 @@ void print_va_list_buffered(const string fmt, va_list args) {
}
// context.allocator (alloc & dealloc)
void prints(const string fmt, ...) {
va_list args;
va_start(args, fmt);
print_va_list_buffered(fmt, args);
va_end(args);
}
// context.allocator (alloc & dealloc)
void printf(const char* fmt, ...) {
va_list args;
va_start(args, fmt);

View file

@ -30,10 +30,13 @@ void log_heap() {
}
void test_allocator(bool do_log_heap) {
Allocator heap = get_heap_allocator();
// Basic allocation and free
int* a = (int*)alloc(sizeof(int));
int* b = (int*)alloc(sizeof(int));
int* c = (int*)alloc(sizeof(int));
int* a = (int*)alloc(heap, sizeof(int));
int* b = (int*)alloc(heap, sizeof(int));
int* c = (int*)alloc(heap, sizeof(int));
*a = 69;
*b = 420;
@ -51,41 +54,41 @@ void test_allocator(bool do_log_heap) {
assert(*c == 1337, "Test failed: Memory corrupted");
// Allocate and free large block
void* large_block = alloc(1024 * 1024 * 100);
dealloc(large_block);
void* large_block = alloc(heap, 1024 * 1024 * 100);
dealloc(heap, large_block);
// Allocate multiple small blocks
void* blocks[100];
for (int i = 0; i < 100; ++i) {
blocks[i] = alloc(128);
blocks[i] = alloc(heap, 128);
assert(blocks[i] != NULL, "Failed to allocate small block");
}
for (int i = 0; i < 100; ++i) {
dealloc(blocks[i]);
dealloc(heap, blocks[i]);
}
// Stress test with various sizes
for (int i = 1; i <= 1000; ++i) {
void* p = alloc(i * 64);
void* p = alloc(heap, i * 64);
assert(p != NULL, "Failed to allocate varying size block");
dealloc(p);
dealloc(heap, p);
}
// Free in reverse order
for (int i = 0; i < 100; ++i) {
blocks[i] = alloc(128);
blocks[i] = alloc(heap, 128);
assert(blocks[i] != NULL, "Failed to allocate small block");
}
for (int i = 99; i >= 0; --i) {
dealloc(blocks[i]);
dealloc(heap, blocks[i]);
}
// Test memory integrity with various allocation patterns
int* nums[10];
for (int i = 0; i < 10; ++i) {
nums[i] = (int*)alloc(sizeof(int) * 10);
nums[i] = (int*)alloc(heap, sizeof(int) * 10);
for (int j = 0; j < 10; ++j) {
nums[i][j] = i * 10 + j;
}
@ -95,19 +98,17 @@ void test_allocator(bool do_log_heap) {
for (int j = 0; j < 10; ++j) {
assert(nums[i][j] == i * 10 + j, "Memory corruption detected");
}
dealloc(nums[i]);
dealloc(heap, nums[i]);
}
reset_temporary_storage();
push_allocator(temp);
int* foo = (int*)alloc(72);
int* foo = (int*)alloc(temp, 72);
*foo = 1337;
void* bar = alloc(69);
void* bar = alloc(temp, 69);
(void)bar;
void* baz = alloc(420);
void* baz = alloc(temp, 420);
(void)baz;
assert(*foo == 1337, "Temp memory corruptada");
@ -116,63 +117,61 @@ void test_allocator(bool do_log_heap) {
reset_temporary_storage();
foo = (int*)alloc(72);
foo = (int*)alloc(temp, 72);
assert(old_foo == foo, "Temp allocator goof");
pop_allocator();
// Repeated Allocation and Free
for (int i = 0; i < 10000; ++i) {
void* temp = alloc(128);
void* temp = alloc(heap, 128);
assert(temp != NULL && "Repeated allocation failed");
dealloc(temp);
dealloc(heap, temp);
}
// Mixed Size Allocations
void* mixed_blocks[200];
for (int i = 0; i < 200; ++i) {
if (i % 2 == 0) {
mixed_blocks[i] = alloc(128);
mixed_blocks[i] = alloc(heap, 128);
} else {
mixed_blocks[i] = alloc(1024 * 1024); // 1MB blocks
mixed_blocks[i] = alloc(heap, 1024 * 1024); // 1MB blocks
}
assert(mixed_blocks[i] != NULL && "Mixed size allocation failed");
}
for (int i = 0; i < 200; ++i) {
if (i % 2 == 0) {
dealloc(mixed_blocks[i]);
dealloc(heap, mixed_blocks[i]);
}
}
for (int i = 0; i < 200; ++i) {
if (i % 2 != 0) {
dealloc(mixed_blocks[i]);
dealloc(heap, mixed_blocks[i]);
}
}
// Fragmentation Stress Test
for (int i = 0; i < 50; ++i) {
blocks[i] = alloc(256);
blocks[i] = alloc(heap, 256);
assert(blocks[i] != NULL && "Failed to allocate small block for fragmentation test");
}
for (int i = 0; i < 50; i += 2) {
dealloc(blocks[i]);
dealloc(heap, blocks[i]);
}
for (int i = 50; i < 100; ++i) {
blocks[i] = alloc(128);
blocks[i] = alloc(heap, 128);
assert(blocks[i] != NULL && "Failed to allocate small block in fragmented heap");
}
for (int i = 50; i < 100; ++i) {
dealloc(blocks[i]);
dealloc(heap, blocks[i]);
}
for (int i = 1; i < 50; i += 2) {
dealloc(blocks[i]);
dealloc(heap, blocks[i]);
}
if (do_log_heap) log_heap();
@ -192,7 +191,8 @@ void test_thread_proc1(Thread* t) {
}
void test_threads() {
Thread* t = os_make_thread(test_thread_proc1);
Thread* t = os_make_thread(test_thread_proc1, get_heap_allocator());
os_start_thread(t);
os_sleep(20);
printf("This should be printed in middle of thread execution\n");
@ -205,31 +205,34 @@ void test_threads() {
}
void test_allocator_threaded(Thread *t) {
Allocator heap = get_heap_allocator();
for (int i = 0; i < 1000; ++i) {
void* temp = alloc(128);
void* temp = alloc(heap, 128);
assert(temp != NULL && "Repeated allocation failed");
dealloc(temp);
dealloc(heap, temp);
}
void* mixed_blocks[40];
for (int i = 0; i < 40; ++i) {
if (i % 2 == 0) {
mixed_blocks[i] = alloc(128);
mixed_blocks[i] = alloc(heap, 128);
} else {
mixed_blocks[i] = alloc(1024 * 1024); // 1MB blocks
mixed_blocks[i] = alloc(heap, 1024 * 1024); // 1MB blocks
}
assert(mixed_blocks[i] != NULL && "Mixed size allocation failed");
}
for (int i = 0; i < 40; ++i) {
if (i % 2 == 0) {
dealloc(mixed_blocks[i]);
dealloc(heap, mixed_blocks[i]);
}
}
for (int i = 0; i < 40; ++i) {
if (i % 2 != 0) {
dealloc(mixed_blocks[i]);
dealloc(heap, mixed_blocks[i]);
}
}
}
@ -239,43 +242,43 @@ void test_strings() {
assert(length_of_null_terminated_string("Test") == 4, "Failed: length_of_null_terminated_string");
assert(length_of_null_terminated_string("") == 0, "Failed: length_of_null_terminated_string");
Allocator heap = get_heap_allocator();
// Test alloc_string and dealloc_string
string alloc_str = alloc_string(10);
string alloc_str = alloc_string(heap, 10);
assert(alloc_str.data != NULL, "Failed: alloc_string");
assert(alloc_str.count == 10, "Failed: alloc_string");
dealloc_string(alloc_str);
dealloc_string(heap, alloc_str);
// Test string_concat
string str1 = fixed_string("Hello, ");
string str2 = fixed_string("World!");
string concat_str = string_concat(str1, str2);
string concat_str = string_concat(str1, str2, heap);
assert(concat_str.count == str1.count + str2.count, "Failed: string_concat");
assert(memcmp(concat_str.data, "Hello, World!", concat_str.count) == 0, "Failed: string_concat");
dealloc_string(concat_str);
dealloc_string(heap, concat_str);
// Test convert_to_null_terminated_string
char* cstr = convert_to_null_terminated_string(str1);
char* cstr = convert_to_null_terminated_string(str1, heap);
assert(strcmp(cstr, "Hello, ") == 0, "Failed: convert_to_null_terminated_string");
dealloc(cstr);
dealloc(heap, cstr);
// Test temp_convert_to_null_terminated_string
cstr = temp_convert_to_null_terminated_string(str2);
assert(strcmp(cstr, "World!") == 0, "Failed: temp_convert_to_null_terminated_string");
// No need to dealloc, it's temporary storage
// Test sprint
string format_str = fixed_string("Number: %d");
string formatted_str = sprint(format_str, 42);
char* formatted_cstr = convert_to_null_terminated_string(formatted_str);
string formatted_str = sprint(heap, format_str, 42);
char* formatted_cstr = convert_to_null_terminated_string(formatted_str, heap);
assert(strcmp(formatted_cstr, "Number: 42") == 0, "Failed: sprint");
dealloc(formatted_str.data);
dealloc(formatted_cstr);
dealloc(heap, formatted_str.data);
dealloc(heap, formatted_cstr);
// Test tprint
string temp_formatted_str = tprint(format_str, 100);
formatted_cstr = temp_convert_to_null_terminated_string(temp_formatted_str);
assert(strcmp(formatted_cstr, "Number: 100") == 0, "Failed: tprint");
// No need to dealloc, it's temporary storage
// Test print and printf (visual inspection)
printf("Expected output: Hello, World!\n");
@ -308,24 +311,24 @@ void test_strings() {
// Test handling of empty strings
string empty_str = fixed_string("");
string concat_empty_str = string_concat(empty_str, empty_str);
string concat_empty_str = string_concat(empty_str, empty_str, heap);
assert(concat_empty_str.count == 0, "Failed: string_concat with empty strings");
dealloc_string(concat_empty_str);
dealloc_string(heap, concat_empty_str);
// Test very large strings (performance test)
string large_str1 = alloc_string(1024 * 1024);
string large_str2 = alloc_string(1024 * 1024);
string large_concat_str = string_concat(large_str1, large_str2);
string large_str1 = alloc_string(heap, 1024 * 1024);
string large_str2 = alloc_string(heap, 1024 * 1024);
string large_concat_str = string_concat(large_str1, large_str2, heap);
assert(large_concat_str.count == 2 * 1024 * 1024, "Failed: large string_concat");
dealloc_string(large_str1);
dealloc_string(large_str2);
dealloc_string(large_concat_str);
dealloc_string(heap, large_str1);
dealloc_string(heap, large_str2);
dealloc_string(heap, large_concat_str);
// Test string with special characters
string special_char_str = fixed_string("Special chars: \n\t\r");
cstr = convert_to_null_terminated_string(special_char_str);
cstr = convert_to_null_terminated_string(special_char_str, heap);
assert(strcmp(cstr, "Special chars: \n\t\r") == 0, "Failed: special character string");
dealloc(cstr);
dealloc(heap, cstr);
string a = tprintf("Hello, %cs!\n", "balls");
string balls1 = string_view(a, 7, 5);
@ -340,17 +343,15 @@ void test_file_io() {
#if TARGET_OS == WINDOWS
// Test win32_fixed_utf8_to_null_terminated_wide
string utf8_str = fixed_string("Test");
u16 *wide_str = win32_fixed_utf8_to_null_terminated_wide(utf8_str);
u16 *wide_str = win32_fixed_utf8_to_null_terminated_wide(utf8_str, get_heap_allocator());
assert(wide_str != NULL, "Failed: win32_fixed_utf8_to_null_terminated_wide");
assert(wide_str[4] == 0, "Failed: win32_fixed_utf8_to_null_terminated_wide");
dealloc(wide_str);
dealloc(get_heap_allocator(), wide_str);
// Test temp_win32_fixed_utf8_to_null_terminated_wide
push_temp_allocator();
wide_str = temp_win32_fixed_utf8_to_null_terminated_wide(utf8_str);
assert(wide_str != NULL, "Failed: temp_win32_fixed_utf8_to_null_terminated_wide");
assert(wide_str[4] == 0, "Failed: temp_win32_fixed_utf8_to_null_terminated_wide");
pop_allocator();
#endif
File file = OS_INVALID_FILE;
@ -391,12 +392,14 @@ void test_file_io() {
bool write_entire_result = os_write_entire_file(fixed_string("entire_test.txt"), write_data);
assert(write_entire_result, "Failed: os_write_entire_file");
Allocator heap = get_heap_allocator();
string read_data;
bool read_entire_result = os_read_entire_file(fixed_string("entire_test.txt"), &read_data);
bool read_entire_result = os_read_entire_file(fixed_string("entire_test.txt"), &read_data, heap);
assert(read_entire_result, "Failed: os_read_entire_file");
assert(strings_match(read_data, write_data), "Failed: os_read_entire_file write/read mismatch");
assert(memcmp(read_data.data, write_data.data, write_data.count) == 0, "Failed: os_read_entire_file (content mismatch)");
dealloc(read_data.data);
dealloc(heap, read_data.data);
// Test fprint
File balls = os_file_open(fixed_string("balls.txt"), O_WRITE | O_CREATE);
@ -404,7 +407,7 @@ void test_file_io() {
fprint(balls, "Hello, %cs!", "Balls");
os_file_close(balls);
string hello_balls;
read_entire_result = os_read_entire_file(fixed_string("balls.txt"), &hello_balls);
read_entire_result = os_read_entire_file(fixed_string("balls.txt"), &hello_balls, heap);
assert(read_entire_result, "Failed: could not read balls.txt");
assert(strings_match(hello_balls, fixed_string("Hello, Balls!")), "Failed: balls read/write mismatch. Expected 'Hello, Balls!', got '%s'", hello_balls);
@ -419,7 +422,7 @@ void test_file_io() {
assert(ok, "write integers fail");
string integers_read;
ok = os_read_entire_file(fxstr("integers"), &integers_read);
ok = os_read_entire_file(fxstr("integers"), &integers_read, heap);
assert(ok, "read integers fail");
u64 *new_integers = (u64*)integers_data.data;
assert(integers_read.count == integers_data.count, "Failed: big file read/write mismatch. Read was %d and written was %d", integers_read.count, integers_data.count);
@ -435,6 +438,8 @@ void test_file_io() {
assert(delete_ok, "Failed: could not delete entire_test.txt");
delete_ok = os_file_delete(fixed_string("balls.txt"));
assert(delete_ok, "Failed: could not delete balls.txt");
delete_ok = os_file_delete(fixed_string("integers.txt"));
assert(delete_ok, "Failed: could not delete integers.txt");
}
void oogabooga_run_tests() {
@ -453,7 +458,7 @@ void oogabooga_run_tests() {
printf("Thread bombing allocator... ");
Thread* threads[100];
for (int i = 0; i < 100; i++) {
threads[i] = os_make_thread(test_allocator_threaded);
threads[i] = os_make_thread(test_allocator_threaded, get_heap_allocator());
os_start_thread(threads[i]);
}
for (int i = 0; i < 100; i++) {