jc/memory/allocators.jai

137 lines
3.6 KiB
Text

#module_parameters(RUN_TESTS := false);
make_crash_allocator :: () -> Allocator {
return .{ proc = crash_allocator_proc };
}
crash_allocator_proc :: (mode: Allocator_Mode, size: s64, old_size: s64, old_memory: *void, allocator_data: *void) -> *void {
message: string;
if mode == {
case .ALLOCATE;
message = basic.tprint("Attempt to allocate % byte(s) using the crash allocator!", size);
case .RESIZE;
message = basic.tprint("Attempt to resize (from % to % byte(s)) using the crash allocator!", old_size, size);
case .FREE;
message = basic.tprint("Attempt to free % byte(s) using the crash allocator!", size);
}
loc := meta.get_stack_trace_caller_location();
basic.assert(false, message, loc = loc);
debug_break();
return null;
}
Arena :: struct {
memory: *void;
memory_size: u64;
offset: u64;
}
init_arena :: (a: *Arena, memory: *void, size: u64) {
a.memory = memory;
a.memory_size = size;
a.offset = 0;
}
make_arena_allocator :: (arena: *Arena) -> Allocator {
return .{
data = arena,
proc = xx arena_allocator_proc,
};
}
Extended_Allocator_Mode :: enum {
using Allocator_Mode;
request_memory :: Allocator_Mode.ALLOCATE;
resize_memory :: Allocator_Mode.RESIZE;
release_memory :: Allocator_Mode.FREE;
first_time_used :: Allocator_Mode.STARTUP;
release_everything :: Allocator_Mode.SHUTDOWN;
reset_state;
save_point; // should return *s64
restore_save_point; // 'old_size' will be the dereferenced return value of 'save_point'
}
arena_allocator_proc :: (mode: Extended_Allocator_Mode, size: s64, old_size: s64, old_memory: *void, allocator_data: *void) -> *void {
arena := allocator_data.(*Arena);
if mode == {
case .request_memory;
return arena_alloc(arena, size);
case .resize_memory;
if old_memory == null {
return arena_alloc(arena, size);
}
if size == 0 {
return null;
}
if size == old_size {
return old_memory;
}
new_memory := arena_alloc(arena, size);
memcpy(new_memory, old_memory, old_size);
return new_memory;
case .save_point;
return *arena.offset;
case .restore_save_point;
arena.offset = old_size.(u64);
}
return null;
}
arena_alloc :: (a: *Arena, count: int, alignment := mem.Default_Align, loc := #caller_location) -> *void {
basic.assert(a.memory != null, "arena: not initialized", loc = loc);
basic.assert(mem.power_of_two(alignment));
end := a.memory.(*u8) + a.offset;
ptr := mem.align_to(end.(int), alignment);
total_size := (count + ptr.(*u8) - end.(*u8)).(u64);
basic.assert(a.offset + total_size <= a.memory_size, "arena: out of memory", loc = loc);
a.offset += total_size;
return ptr.(*void);
}
#scope_file;
mem :: #import "jc/memory";
meta :: #import "jc/meta";
basic :: #import "Basic"; // @future
// ----------------------------------------------------------
// TESTS
// ----------------------------------------------------------
#if RUN_TESTS #run {
test :: #import "jc/meta/test";
test.run("arena:basic", t => {
memory := mem.request_memory(1 * mem.Kilobyte);
defer mem.release_memory(memory);
arena: Arena;
init_arena(*arena, memory, 1 * mem.Kilobyte);
context.allocator = make_arena_allocator(*arena);
save_point := mem.allocator_save();
i := mem.request_memory(int);
basic.assert(i != null);
basic.assert(arena.offset == size_of(int));
mem.allocator_restore(save_point);
});
}