...
This commit is contained in:
@@ -1377,5 +1377,86 @@ END;
|
||||
print("{}\n", "piped" |> concat(" ok") |> concat("!")); // piped ok!
|
||||
}
|
||||
|
||||
// ── alloc_slice ──────────────────────────────────────────
|
||||
{
|
||||
items := alloc_slice(s64, 5);
|
||||
items[0] = 10;
|
||||
items[1] = 20;
|
||||
items[2] = 30;
|
||||
items[3] = 40;
|
||||
items[4] = 50;
|
||||
print("alloc len: {}\n", items.len); // alloc len: 5
|
||||
print("alloc[0]: {}\n", items[0]); // alloc[0]: 10
|
||||
print("alloc[4]: {}\n", items[4]); // alloc[4]: 50
|
||||
|
||||
// alloc_slice with u8
|
||||
bytes := alloc_slice(u8, 3);
|
||||
bytes[0] = 65;
|
||||
bytes[1] = 66;
|
||||
bytes[2] = 67;
|
||||
print("bytes len: {}\n", bytes.len); // bytes len: 3
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
// ALLOCATORS
|
||||
// ========================================================
|
||||
print("--- allocators ---\n");
|
||||
|
||||
// ── GPA ─────────────────────────────────────────────────
|
||||
{
|
||||
gpa_state : GPA = .{ alloc_count = 0 };
|
||||
gpa := gpa_create(@gpa_state);
|
||||
p1 := gpa.alloc(gpa.ctx, 64);
|
||||
p2 := gpa.alloc(gpa.ctx, 128);
|
||||
print("gpa allocs: {}\n", gpa_state.alloc_count); // gpa allocs: 2
|
||||
gpa.free(gpa.ctx, p1);
|
||||
gpa.free(gpa.ctx, p2);
|
||||
print("gpa final: {}\n", gpa_state.alloc_count); // gpa final: 0
|
||||
}
|
||||
|
||||
// ── Arena backed by GPA (multi-chunk) ───────────────────
|
||||
{
|
||||
gpa_state3 : GPA = .{ alloc_count = 0 };
|
||||
gpa3 := gpa_create(@gpa_state3);
|
||||
arena_state : Arena = ---;
|
||||
arena := arena_create(@arena_state, gpa3, 32);
|
||||
// First chunk fits 80 usable bytes
|
||||
a1 := arena.alloc(arena.ctx, 40);
|
||||
a2 := arena.alloc(arena.ctx, 40);
|
||||
print("arena chunks: {}\n", gpa_state3.alloc_count); // arena chunks: 1
|
||||
// Overflow → new chunk
|
||||
a3 := arena.alloc(arena.ctx, 16);
|
||||
print("arena overflow: {}\n", gpa_state3.alloc_count); // arena overflow: 2
|
||||
// Verify memory works across chunks
|
||||
p1 : [*]u8 = xx a1;
|
||||
p3 : [*]u8 = xx a3;
|
||||
p1[0] = 42;
|
||||
p3[0] = 99;
|
||||
print("arena a1: {}\n", p1[0]); // arena a1: 42
|
||||
print("arena a3: {}\n", p3[0]); // arena a3: 99
|
||||
// Reset retains newest chunk
|
||||
arena_reset(@arena_state);
|
||||
print("arena reset idx: {}\n", arena_state.end_index); // arena reset idx: 0
|
||||
print("arena reset gpa: {}\n", gpa_state3.alloc_count);// arena reset gpa: 1
|
||||
// Deinit frees all
|
||||
arena_deinit(@arena_state);
|
||||
print("arena deinit: {}\n", gpa_state3.alloc_count); // arena deinit: 0
|
||||
}
|
||||
|
||||
// ── BufAlloc from stack array ───────────────────────────
|
||||
{
|
||||
stack_buf : [128]u8 = ---;
|
||||
buf_state : BufAlloc = ---;
|
||||
bufalloc := buf_create(@buf_state, @stack_buf[0], 128);
|
||||
b1 := bufalloc.alloc(bufalloc.ctx, 24);
|
||||
b2 := bufalloc.alloc(bufalloc.ctx, 24);
|
||||
print("buf pos: {}\n", buf_state.pos); // buf pos: 48
|
||||
b3 := bufalloc.alloc(bufalloc.ctx, 200);
|
||||
b3_i : s64 = xx b3;
|
||||
print("buf overflow: {}\n", b3_i); // buf overflow: 0
|
||||
buf_reset(@buf_state);
|
||||
print("buf reset: {}\n", buf_state.pos); // buf reset: 0
|
||||
}
|
||||
|
||||
print("=== DONE ===\n");
|
||||
}
|
||||
|
||||
154
examples/modules/allocators.sx
Normal file
154
examples/modules/allocators.sx
Normal file
@@ -0,0 +1,154 @@
|
||||
#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;
|
||||
}
|
||||
@@ -18,57 +18,37 @@ field_value_int :: ($T: Type, idx: s64) -> s64 #builtin;
|
||||
field_index :: ($T: Type, val: T) -> s64 #builtin;
|
||||
string :: []u8 #builtin;
|
||||
|
||||
// --- Arena allocator & Context ---
|
||||
#import "allocators.sx";
|
||||
|
||||
Arena :: struct {
|
||||
buf: string;
|
||||
pos: s64;
|
||||
}
|
||||
// --- Context ---
|
||||
|
||||
Context :: struct {
|
||||
arena: *Arena;
|
||||
allocator: Allocator;
|
||||
data: *void;
|
||||
}
|
||||
|
||||
context : Context = ---;
|
||||
|
||||
arena_create :: (size: s64) -> Arena {
|
||||
Arena.{ buf = cstring(size), pos = 0 };
|
||||
}
|
||||
|
||||
arena_alloc :: (arena: *Arena, size: s64) -> *void {
|
||||
aligned := (size + 7) & (0 - 8);
|
||||
if arena.pos + aligned > arena.buf.len {
|
||||
return malloc(aligned);
|
||||
}
|
||||
ptr : *void = xx @arena.buf[arena.pos];
|
||||
arena.pos = arena.pos + aligned;
|
||||
ptr;
|
||||
}
|
||||
|
||||
arena_reset :: (arena: *Arena) {
|
||||
arena.pos = 0;
|
||||
}
|
||||
|
||||
arena_destroy :: (arena: *Arena) {
|
||||
free(arena.buf.ptr);
|
||||
}
|
||||
|
||||
// --- String allocation ---
|
||||
|
||||
CString :: union {
|
||||
s: string;
|
||||
struct { ptr: *void; len: s64; };
|
||||
}
|
||||
// --- Slice & string allocation ---
|
||||
|
||||
cstring :: (size: s64) -> string {
|
||||
p : s64 = xx context.arena;
|
||||
raw := if p != 0 then arena_alloc(context.arena, size + 1) else malloc(size + 1);
|
||||
p : s64 = xx context.allocator.ctx;
|
||||
raw := if p != 0 then context.allocator.alloc(context.allocator.ctx, size + 1) else malloc(size + 1);
|
||||
memset(raw, 0, size + 1);
|
||||
rs : CString = ---;
|
||||
rs.ptr = raw;
|
||||
rs.len = size;
|
||||
rs.s;
|
||||
s : string = ---;
|
||||
s.ptr = xx raw;
|
||||
s.len = size;
|
||||
s;
|
||||
}
|
||||
|
||||
alloc_slice :: ($T: Type, count: s64) -> []T {
|
||||
p : s64 = xx context.allocator.ctx;
|
||||
raw := if p != 0 then context.allocator.alloc(context.allocator.ctx, count * size_of(T)) else malloc(count * size_of(T));
|
||||
memset(raw, 0, count * size_of(T));
|
||||
s : []T = ---;
|
||||
s.ptr = xx raw;
|
||||
s.len = count;
|
||||
s;
|
||||
}
|
||||
|
||||
int_to_string :: (n: s64) -> string {
|
||||
|
||||
Reference in New Issue
Block a user