Files
game/modules/allocators.sx
2026-02-22 13:22:29 +02:00

156 lines
3.3 KiB
Plaintext

#import "std.sx";
// --- Allocator protocol ---
Allocator :: struct {
ctx: *void;
alloc_fn: (*void, s64) -> *void;
free_fn: (*void, *void) -> void;
}
allocator_alloc :: (a: Allocator, size: s64) -> *void {
a.alloc_fn(a.ctx, size);
}
allocator_dealloc :: (a: Allocator, ptr: *void) {
a.free_fn(a.ctx, ptr);
}
alloc :: ufcs allocator_alloc;
dealloc :: ufcs allocator_dealloc;
// --- GPA: general purpose allocator (malloc/free wrapper) ---
GPA :: struct {
alloc_count: s64;
}
gpa_alloc :: (ctx: *void, size: s64) -> *void {
gpa : *GPA = xx ctx;
gpa.alloc_count += 1;
malloc(size);
}
gpa_free :: (ctx: *void, ptr: *void) {
gpa : *GPA = xx ctx;
gpa.alloc_count -= 1;
free(ptr);
}
gpa_create :: (gpa: *GPA) -> Allocator {
Allocator.{ ctx = gpa, alloc_fn = gpa_alloc, free_fn = gpa_free };
}
// --- Arena: multi-chunk bump allocator (Zig-inspired) ---
ArenaChunk :: struct {
next: *ArenaChunk;
cap: s64;
}
Arena :: struct {
first: *ArenaChunk;
end_index: s64;
parent: Allocator;
}
arena_add_chunk :: (a: *Arena, min_size: s64) {
prev_cap := if a.first != null then a.first.cap else 0;
needed := min_size + 16 + 16;
len := (prev_cap + needed) * 3 / 2;
raw := a.parent.alloc(len);
chunk : *ArenaChunk = xx raw;
chunk.next = a.first;
chunk.cap = len;
a.first = chunk;
a.end_index = 0;
}
arena_alloc :: (ctx: *void, size: s64) -> *void {
a : *Arena = xx ctx;
aligned := (size + 7) & (0 - 8);
if a.first != null {
usable := a.first.cap - 16;
if a.end_index + aligned <= usable {
buf : [*]u8 = xx a.first;
ptr := @buf[16 + a.end_index];
a.end_index = a.end_index + aligned;
return ptr;
}
}
arena_add_chunk(a, aligned);
buf : [*]u8 = xx a.first;
ptr := @buf[16 + a.end_index];
a.end_index = a.end_index + aligned;
ptr;
}
arena_free :: (ctx: *void, ptr: *void) {
}
arena_create :: (a: *Arena, parent: Allocator, size: s64) -> Allocator {
a.first = null;
a.end_index = 0;
a.parent = parent;
arena_add_chunk(a, size);
Allocator.{ ctx = a, alloc_fn = arena_alloc, free_fn = arena_free };
}
arena_reset :: (a: *Arena) {
// Keep first chunk (newest/largest), free the rest
if a.first != null {
it := a.first.next;
while it != null {
next := it.next;
a.parent.dealloc(it);
it = next;
}
a.first.next = null;
}
a.end_index = 0;
}
arena_deinit :: (a: *Arena) {
it := a.first;
while it != null {
next := it.next;
a.parent.dealloc(it);
it = next;
}
a.first = null;
a.end_index = 0;
}
// --- BufAlloc: bump allocator backed by a user-provided slice ---
BufAlloc :: struct {
buf: [*]u8;
len: s64;
pos: s64;
}
buf_alloc :: (ctx: *void, size: s64) -> *void {
b : *BufAlloc = xx ctx;
aligned := (size + 7) & (0 - 8);
if b.pos + aligned > b.len {
return null;
}
ptr := @b.buf[b.pos];
b.pos = b.pos + aligned;
ptr;
}
buf_free :: (ctx: *void, ptr: *void) {
}
buf_create :: (b: *BufAlloc, buf: [*]u8, len: s64) -> Allocator {
b.buf = buf;
b.len = len;
b.pos = 0;
Allocator.{ ctx = b, alloc_fn = buf_alloc, free_fn = buf_free };
}
buf_reset :: (b: *BufAlloc) {
b.pos = 0;
}