#module_parameters(RUN_TESTS := false); Kilobyte :: 1024; Megabyte :: 1024 * Kilobyte; Gigabyte :: 1024 * Megabyte; Default_Align :: #run 2 * align_of(*void); align_of :: ($T: Type) -> int #expand { return #run -> int { info := type_info(struct{ p: u8; t: T; }); return info.members[1].offset_in_bytes.(int); }; } default_of :: ($T: Type) -> T #expand { default: T; return default; } zero_of :: ($T: Type) -> T #expand { zero: T = ---; memset(*zero, 0, size_of(T)); return zero; } bitcast :: ($T: Type, expr: Code) -> T #expand { value := expr; return (*value).(*T).*; } power_of_two :: (x: int) -> bool { if x == 0 return false; return x & (x - 1) == 0; } align_to :: (ptr: int, align: int = Default_Align) -> int { basic.assert(power_of_two(align), "alignment must be a power of two"); p := ptr; mod := p & (align - 1); if mod != 0 then p += align - mod; return p; } init_or_zero :: inline (ptr: *$T, custom_init: (*T) = null) { init :: initializer_of(T); if custom_init != null { custom_init(ptr); } #if init != null { inline init(ptr); } else { memset(ptr, 0, size_of(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_to(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 { T = compiler.get_type(info.element_type); return true; } return false; } { 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); } arr: [..]T; arr.data = data; arr.count = 0; arr.allocated = size / size_of(T); arr.allocator = context.allocator; return arr; } request_memory :: ($T: Type, $init := true) -> *T #modify { return !meta.type_is_array(T); } { ptr := request_memory(size_of(T), align_of(T)).(*T); #if init init_or_zero(ptr); return ptr; } release_memory :: inline (ptr: *void) { allocator := context.allocator; allocator.proc(xx Extended_Allocator_Mode.release_memory, 0, 0, ptr, allocator.data); } release_memory :: inline (arr: []$T) { release_memory(arr.data); } release_memory :: inline (arr: [..]$T) { release_memory(arr.data,, allocator = arr.allocator); } release_memory :: inline (str: string) { release_memory(str.data); } #load "allocators.jai"; #scope_file; meta :: #import "jc/meta"; basic :: #import "Basic"; // @future compiler :: #import "Compiler"; // @future // ---------------------------------------------------------- // TESTS // ---------------------------------------------------------- #if RUN_TESTS { test :: #import "jc/test"; #run { test.run("request_memory:dynamic arrays", (t) => { a1 := request_memory([..]int); defer release_memory(a1); test.expect(t, a1.count == 0); test.expect(t, a1.allocated == 0); basic.array_add(*a1, 10, 20, 30); test.expect(t, a1.count == 3); test.expect(t, a1.allocated != 0, "%", a1.allocated); 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); }); } }