#module_parameters(RUN_TESTS_AT_COMPILE_TIME := 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; } undefined_of :: ($T: Type) -> T #expand { uninit: T = ---; return uninit; } zero_of :: ($T: Type) -> T #expand { zero := undefined_of(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; } next_power_of_two :: (x: int) -> int #no_aoc { basic.assert(power_of_two(x), "value (%) must be a power of two", x); // Bit twiddling hacks next power of two x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; x |= x >> 32; return x + 1; } 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) { if custom_init != null { custom_init(ptr); } initializer :: initializer_of(T); #if initializer { inline initializer(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.(*void)); } release_memory :: inline (arr: [..]$T) { release_memory(arr.data.(*void),, allocator = arr.allocator); } release_memory :: inline (str: string) { release_memory(str.data.(*void)); } // @todo(judah): why can't we use $T/struct{ allocator: Allocator }? lazy_set_allocator :: (thing: *$T, allocator := context.allocator) #modify { info := T.(*Type_Info_Struct); ok := false; if info.type == .STRUCT ok = true; if ok for info.members if it.name == "allocator" && it.type == Allocator.(*Type_Info) { ok = true; break; } return ok, "can only set allocator on struct with allocator field or dynamic array"; } #expand { if thing.allocator.proc == null { thing.allocator = allocator; } } lazy_set_allocator :: (array: *[..]$T, allocator := context.allocator) #expand { if array.allocator.proc == null { array.allocator = allocator; } } #load "allocators.jai"; #scope_module; #if RUN_TESTS_AT_COMPILE_TIME { RUN_TESTS :: true; } #scope_file; meta :: #import "jc/meta"; basic :: #import "Basic"; // @future compiler :: #import "Compiler"; // @future // ---------------------------------------------------------- // TESTS // ---------------------------------------------------------- #if #exists(RUN_TESTS) #run { test :: #import "jc/meta/test"; 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); }); }