mem: typed allocation helpers + drop bare malloc/free (Phase 2.2); resolve 0119 as |>-contract clarification
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user