155 lines
3.7 KiB
Plaintext
155 lines
3.7 KiB
Plaintext
#import "std.sx";
|
|
|
|
// --- Allocator protocol ---
|
|
|
|
Allocator :: struct {
|
|
ctx: *void;
|
|
alloc: (*void, s64) -> *void;
|
|
free: (*void, *void) -> void;
|
|
}
|
|
|
|
// --- 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 {
|
|
ctx : *void = xx gpa;
|
|
Allocator.{ ctx = ctx, alloc = gpa_alloc, free = gpa_free };
|
|
}
|
|
|
|
// --- Arena: multi-chunk bump allocator (Zig-inspired) ---
|
|
|
|
ArenaChunk :: struct {
|
|
next: *void; // *ArenaChunk
|
|
cap: s64; // total chunk size including this header
|
|
}
|
|
|
|
Arena :: struct {
|
|
first: *void; // *ArenaChunk — head of list (newest first)
|
|
end_index: s64; // bump position within current chunk's usable area
|
|
parent: Allocator; // backing allocator
|
|
}
|
|
|
|
arena_add_chunk :: (a: *Arena, min_size: s64) {
|
|
first_i : s64 = xx a.first;
|
|
prev_cap := if first_i != 0 then { c : *ArenaChunk = xx a.first; c.cap; } else 0;
|
|
needed := min_size + 16 + 16;
|
|
len := (prev_cap + needed) * 3 / 2;
|
|
raw := a.parent.alloc(a.parent.ctx, len);
|
|
chunk : *ArenaChunk = xx raw;
|
|
chunk.next = a.first;
|
|
chunk.cap = len;
|
|
a.first = xx chunk;
|
|
a.end_index = 0;
|
|
}
|
|
|
|
arena_alloc :: (ctx: *void, size: s64) -> *void {
|
|
a : *Arena = xx ctx;
|
|
aligned := (size + 7) & (0 - 8);
|
|
first_i : s64 = xx a.first;
|
|
if first_i != 0 {
|
|
chunk : *ArenaChunk = xx a.first;
|
|
usable := chunk.cap - 16;
|
|
if a.end_index + aligned <= usable {
|
|
buf : [*]u8 = xx a.first;
|
|
ptr : *void = xx @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 : *void = xx @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);
|
|
ctx : *void = xx a;
|
|
Allocator.{ ctx = ctx, alloc = arena_alloc, free = arena_free };
|
|
}
|
|
|
|
arena_reset :: (a: *Arena) {
|
|
// Keep first chunk (newest/largest), free the rest
|
|
first_i : s64 = xx a.first;
|
|
if first_i != 0 {
|
|
chunk : *ArenaChunk = xx a.first;
|
|
it : s64 = xx chunk.next;
|
|
while it != 0 {
|
|
c : *ArenaChunk = xx it;
|
|
next_i : s64 = xx c.next;
|
|
a.parent.free(a.parent.ctx, xx c);
|
|
it = next_i;
|
|
}
|
|
chunk.next = null;
|
|
}
|
|
a.end_index = 0;
|
|
}
|
|
|
|
arena_deinit :: (a: *Arena) {
|
|
it : s64 = xx a.first;
|
|
while it != 0 {
|
|
c : *ArenaChunk = xx it;
|
|
next_i : s64 = xx c.next;
|
|
a.parent.free(a.parent.ctx, xx c);
|
|
it = next_i;
|
|
}
|
|
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 : *void = xx @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;
|
|
ctx : *void = xx b;
|
|
Allocator.{ ctx = ctx, alloc = buf_alloc, free = buf_free };
|
|
}
|
|
|
|
buf_reset :: (b: *BufAlloc) {
|
|
b.pos = 0;
|
|
}
|