mem: typed allocation helpers + drop bare malloc/free (Phase 2.2); resolve 0119 as |>-contract clarification

This commit is contained in:
agra
2026-06-11 16:17:39 +03:00
parent 3e10809d7e
commit 84e0fb0752
51 changed files with 33256 additions and 32716 deletions

View File

@@ -6,14 +6,15 @@ out :: (str: string) -> void #builtin;
size_of :: ($T: Type) -> s64 #builtin;
align_of :: ($T: Type) -> s64 #builtin;
// Low-level libc bindings, used by allocator implementations to avoid
// recursing through `context.allocator`.
// recursing through `context.allocator`. The bare `malloc`/`free`
// spellings are NOT declared: the Allocator protocol + the std/mem.sx
// helpers are the allocation surface (`free` is the typed slice helper
// there). Raw libc escape hatch: `libc_malloc` / `libc_free`.
libc_malloc :: (size: s64) -> *void #foreign libc "malloc";
libc_free :: (ptr: *void) -> void #foreign libc "free";
malloc :: (size: s64) -> *void #foreign libc "malloc";
memcpy :: (dst: *void, src: *void, size: s64) -> *void #foreign libc "memcpy";
memset :: (dst: *void, val: s64, size: s64) -> void #foreign libc "memset";
free :: (ptr: *void) -> void #foreign libc "free";
type_of :: (val: $T) -> Type #builtin;
type_name :: ($T: Type) -> string #builtin;
field_count :: ($T: Type) -> s64 #builtin;

View File

@@ -1,5 +1,85 @@
#import "modules/std.sx";
// --- Typed allocation helpers ---
//
// The user-facing allocation surface over the Allocator protocol's
// bytes-level primitives (`alloc_bytes` / `dealloc_bytes`). Free
// functions — call directly or fluently via the pipe operator:
//
// s := context.allocator |> create(Session);
// s.* = Session.{}; // no zero-init (Zig-aligned)
// defer context.allocator |> destroy(s);
//
// moves := context.allocator |> alloc(Move, 64);
// defer context.allocator |> free(moves);
//
// copied := bytes |> clone(context.allocator);
//
// Bodies are complete for the 2-method protocol era: `mem_realloc` is
// alloc+copy+dealloc (the only shape without resize/remap primitives),
// and `align` is accepted for signature stability — alignment-aware
// allocation lands with the protocol expansion.
// Allocate one T. Contents are UNINITIALISED — assign before reading.
create :: (a: Allocator, $T: Type) -> *T {
xx a.alloc_bytes(size_of(T))
}
// Free a *T obtained from `create`.
destroy :: (a: Allocator, ptr: *$T) {
a.dealloc_bytes(xx ptr);
}
// Allocate a []T of `count` elements. Contents are UNINITIALISED.
alloc :: (a: Allocator, $T: Type, count: s64) -> []T {
raw := a.alloc_bytes(count * size_of(T));
s : []T = ---;
s.ptr = xx raw;
s.len = count;
s
}
// Free a []T obtained from `alloc` / `clone` / `resize`.
free :: (a: Allocator, slice: []$T) {
a.dealloc_bytes(xx slice.ptr);
}
// Copy a slice into fresh storage owned by `a`.
clone :: (src: []$T, a: Allocator) -> []T {
raw := a.alloc_bytes(src.len * size_of(T));
memcpy(raw, xx src.ptr, src.len * size_of(T));
s : []T = ---;
s.ptr = xx raw;
s.len = src.len;
s
}
// Reallocate a slice to `new_count` elements: fresh storage, contents
// copied up to min(len, new_count), old backing freed. The returned
// slice replaces the operand — the old slice is dangling after this.
resize :: (slice: []$T, a: Allocator, new_count: s64) -> []T {
raw := a.alloc_bytes(new_count * size_of(T));
n := if slice.len < new_count then slice.len else new_count;
memcpy(raw, xx slice.ptr, n * size_of(T));
a.dealloc_bytes(xx slice.ptr);
s : []T = ---;
s.ptr = xx raw;
s.len = new_count;
s
}
// Bytes-level realloc. 2-method era: alloc + copy(min(old,new)) +
// dealloc — there is no in-place grow primitive to try yet, and
// `align` beyond the heap's natural 8 is not honored until the
// protocol carries alignment.
mem_realloc :: (a: Allocator, ptr: *void, old: s64, new: s64, align: s64) -> *void {
raw := a.alloc_bytes(new);
n := if old < new then old else new;
memcpy(raw, ptr, n);
a.dealloc_bytes(ptr);
raw
}
// --- CAllocator: stateless allocator that delegates directly to libc ---
//
// Zero-sized struct. Used as the default `context.allocator` at program