From 657dec64152f42739306f770b5a31eb563dcfbd3 Mon Sep 17 00:00:00 2001 From: Judah Caruso Date: Sat, 24 May 2025 12:59:52 -0600 Subject: [PATCH] improve memory module --- memory/allocators.jai | 135 ++++++++++++++++++++++++++++++++++++ memory/module.jai | 156 ++++++++++++++++++------------------------ meta/module.jai | 19 +++++ 3 files changed, 219 insertions(+), 91 deletions(-) create mode 100644 memory/allocators.jai diff --git a/memory/allocators.jai b/memory/allocators.jai new file mode 100644 index 0000000..3002246 --- /dev/null +++ b/memory/allocators.jai @@ -0,0 +1,135 @@ +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 := Default_Align, loc := #caller_location) -> *void { + basic.assert(a.memory != null, "arena: not initialized", loc = loc); + basic.assert(power_of_two(alignment)); + + end := a.memory.(*u8) + a.offset; + ptr := align_forward(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; + +basic :: #import "Basic"; // @future +meta :: #import "jc/meta"; + + +// ---------------------------------------------------------- +// TESTS +// ---------------------------------------------------------- + +#if RUN_TESTS { + test :: #import "jc/test"; + + #run { + test.run("arena:basic", t => { + memory := request_memory(1 * Kilobyte); + defer release_memory(memory); + + arena: Arena; + init_arena(*arena, memory, 1 * Kilobyte); + + context.allocator = make_arena_allocator(*arena); + save_point := allocator_save(); + + i := request_memory(int); + basic.assert(i != null); + basic.assert(arena.offset == size_of(int)); + allocator_restore(save_point); + }); + } +} diff --git a/memory/module.jai b/memory/module.jai index 4380111..166b207 100644 --- a/memory/module.jai +++ b/memory/module.jai @@ -6,10 +6,10 @@ Gigabyte :: 1024 * Megabyte; Default_Align :: #run 2 * align_of(*void); -align_of :: ($T: Type) -> u64 #expand { - return #run -> u64 { +align_of :: ($T: Type) -> int #expand { + return #run -> int { info := type_info(struct{ p: u8; t: T; }); - return info.members[1].offset_in_bytes.(u64); + return info.members[1].offset_in_bytes.(int); }; } @@ -29,12 +29,12 @@ bitcast :: ($T: Type, expr: Code) -> T #expand { return (*value).(*T).*; } -power_of_two :: (x: u64) -> bool { +power_of_two :: (x: int) -> bool { if x == 0 return false; return x & (x - 1) == 0; } -align_forward :: (ptr: u64, align: u64 = Default_Align) -> u64 { +align_forward :: (ptr: int, align: int = Default_Align) -> int { basic.assert(power_of_two(align), "alignment must be a power of two"); p := ptr; @@ -57,7 +57,28 @@ init_or_zero :: inline (ptr: *$T, custom_init: (*T) = null) { } } -make :: ($T: Type, reserved := 0, $init := true) -> [..]T +allocator_reset :: () { + allocator := context.allocator; + allocator.proc(xx Extended_Allocator_Mode.reset_state, 0, 0, null, allocator.data); +} + +allocator_save :: () -> int { + allocator := context.allocator; + return allocator.proc(xx Extended_Allocator_Mode.save_point, 0, 0, null, allocator.data).(*int).*; +} + +allocator_restore :: (save_point: int) { + allocator := context.allocator; + allocator.proc(xx Extended_Allocator_Mode.restore_save_point, 0, save_point, null, allocator.data); +} + +request_memory :: (size: int, align := Default_Align) -> *void { + allocator := context.allocator; + aligned_size := align_forward(size, align); + return allocator.proc(xx Extended_Allocator_Mode.request_memory, aligned_size.(int), 0, null, allocator.data); +} + +request_memory :: ($T: Type, reserved := 0, $init := true) -> [..]T #modify { ok, info := meta.type_is_array(T); if ok && info.array_type == .RESIZABLE { @@ -68,9 +89,8 @@ make :: ($T: Type, reserved := 0, $init := true) -> [..]T return false; } { - size := align_forward(size_of(T) * basic.max(reserved, 0).(u64)).(s64); - data := basic.alloc(size); - + size := size_of(T) * basic.max(reserved, 0); + data := request_memory(size, align_of(T)).(*T); #if init if size != 0 { memset(data, 0, size); } @@ -83,86 +103,32 @@ make :: ($T: Type, reserved := 0, $init := true) -> [..]T return arr; } -make :: ($T: Type, $init := true) -> *T +request_memory :: ($T: Type, $init := true) -> *T #modify { return !meta.type_is_array(T); } { - ptr := basic.alloc(size_of(T)).(*T); + ptr := request_memory(size_of(T), align_of(T)).(*T); #if init init_or_zero(ptr); return ptr; } -Crash_Allocator :: Allocator.{ 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); - } - - basic.assert(false, message); - debug_break(); - return null; +release_memory :: inline (ptr: *void) { + allocator := context.allocator; + allocator.proc(xx Extended_Allocator_Mode.release_memory, 0, 0, ptr, allocator.data); } -Arena :: struct { - memory: *void; - memory_size: u64; - offset: u64; +release_memory :: inline (arr: []$T) { + release_memory(arr.data); } -init_arena :: (a: *Arena, memory: *void, size: u64) { - a.memory = memory; - a.memory_size = size; - a.offset = 0; +release_memory :: inline (arr: [..]$T) { + release_memory(arr.data,, allocator = arr.allocator); } -arena_allocator_proc :: (mode: Allocator_Mode, size: s64, old_size: s64, old_memory: *void, allocator_data: *void) -> *void { - arena := allocator_data.(*Arena); - if mode == { - case .ALLOCATE; - return arena_alloc(arena, size); - - case .RESIZE; - 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; - } - - return null; -} - -arena_alloc :: (a: *Arena, count: int, alignment := Default_Align, loc := #caller_location) -> *void { - basic.assert(a.memory != null, "arena: not initialized", loc = loc); - basic.assert(power_of_two(alignment)); - - end := a.memory.(*u8) + a.offset; - ptr := align_forward(end.(u64), 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); +release_memory :: inline (str: string) { + release_memory(str.data); } +#load "allocators.jai"; #scope_file; @@ -176,25 +142,33 @@ compiler :: #import "Compiler"; // @future // TESTS // ---------------------------------------------------------- -#if RUN_TESTS #run { +#if RUN_TESTS { test :: #import "jc/test"; - test.run("make:dynamic arrays", (t) => { - a1 := make([..]int); - test.expect(t, a1.count == 0); - test.expect(t, a1.allocated == 0); + #run { + test.run("request_memory:dynamic arrays", (t) => { + a1 := request_memory([..]int); + defer release_memory(a1); - basic.array_add(*a1, 10, 20, 30); - test.expect(t, a1.count == 3); - test.expect(t, a1.allocated != 0, "%", a1.allocated); + test.expect(t, a1.count == 0); + test.expect(t, a1.allocated == 0); - a2 := make([..]int, 8); - test.expect(t, a2.count == 0); - test.expect(t, a2.allocated == 8); - }); + basic.array_add(*a1, 10, 20, 30); + test.expect(t, a1.count == 3); + test.expect(t, a1.allocated != 0, "%", a1.allocated); - test.run("make:values", (t) => { - v1 := make(int); - test.expect(t, v1.* == 0); - }); + a2 := request_memory([..]int, 8); + defer release_memory(a2); + + test.expect(t, a2.count == 0); + test.expect(t, a2.allocated == 8); + }); + + test.run("request_memory:values", (t) => { + v1 := request_memory(int); + defer release_memory(v1); + + test.expect(t, v1.* == 0); + }); + } } diff --git a/meta/module.jai b/meta/module.jai index 94230bb..577a952 100644 --- a/meta/module.jai +++ b/meta/module.jai @@ -1,5 +1,24 @@ #module_parameters(RUN_TESTS := false); +get_stack_trace_caller_location :: (loc := #caller_location) -> Source_Code_Location { + if context.stack_trace == null || context.stack_trace.info == null { + return loc; + } + + cur := context.stack_trace; + while cur != null { + if cur.info == null break; + + if cur.info.location.fully_pathed_filename != loc.fully_pathed_filename { + break; + } + + cur = cur.next; + } + + return cur.info.location; +} + // Can be passed directly to using,map remap_snake_to_pascal :: (names: []string) { for names {