#import "std.sx"; // --- Allocator protocol (inline: ctx + fn-ptrs, no vtable indirection) --- Allocator :: protocol #inline { alloc :: (size: s64) -> *void; dealloc :: (ptr: *void); } // --- GPA: general purpose allocator (malloc/free wrapper) --- GPA :: struct { alloc_count: s64; create :: (gpa: *GPA) -> Allocator { xx gpa; } } impl Allocator for GPA { alloc :: (self: *GPA, size: s64) -> *void { self.alloc_count += 1; malloc(size); } dealloc :: (self: *GPA, ptr: *void) { self.alloc_count -= 1; free(ptr); } } // --- Arena: multi-chunk bump allocator --- ArenaChunk :: struct { next: *ArenaChunk; cap: s64; } Arena :: struct { first: *ArenaChunk; end_index: s64; parent: Allocator; 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; } create :: (a: *Arena, parent: Allocator, size: s64) -> Allocator { a.first = null; a.end_index = 0; a.parent = parent; a.add_chunk(size); xx a; } reset :: (a: *Arena) { 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; } 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; } } impl Allocator for Arena { alloc :: (self: *Arena, size: s64) -> *void { aligned := (size + 7) & (0 - 8); if self.first != null { usable := self.first.cap - 16; if self.end_index + aligned <= usable { buf : [*]u8 = xx self.first; ptr := @buf[16 + self.end_index]; self.end_index = self.end_index + aligned; return ptr; } } self.add_chunk(aligned); buf : [*]u8 = xx self.first; ptr := @buf[16 + self.end_index]; self.end_index = self.end_index + aligned; ptr; } dealloc :: (self: *Arena, ptr: *void) {} } // --- BufAlloc: bump allocator backed by a user-provided slice --- BufAlloc :: struct { buf: [*]u8; len: s64; pos: s64; create :: (b: *BufAlloc, buf: [*]u8, len: s64) -> Allocator { b.buf = buf; b.len = len; b.pos = 0; xx b; } reset :: (b: *BufAlloc) { b.pos = 0; } } impl Allocator for BufAlloc { alloc :: (self: *BufAlloc, size: s64) -> *void { aligned := (size + 7) & (0 - 8); if self.pos + aligned > self.len { return null; } ptr := @self.buf[self.pos]; self.pos = self.pos + aligned; ptr; } dealloc :: (self: *BufAlloc, ptr: *void) {} }