lib/os+test: export alloc + free via rt_alloc/rt_free
Add os.alloc(n: u64) *void and os.free(p: *void, n: u64) void as
`export fn` via @symbol("rt_alloc") / @symbol("rt_free"). Signatures
mirror lib/memio's existing internal bindings byte-for-byte — only
the name and `export` keyword change. lib/memio + lib/shlex + lib/
getopt drop their own copies in a follow-up commit.
Doc comment spells out the actual failure ABI: rt_alloc wraps the
raw mmap syscall (no libc), so OOM yields a negative-errno cast to
`*void` (e.g. (void*)-12 for ENOMEM). Neither `== nil` nor the libc
MAP_FAILED `(void*)-1` value catches it; deref faults. A typed
fallible variant is future work (alongside #16 fmt.asprintf).
Test (ostest test_alloc_free_roundtrip, signalled=5): alloc 4096B,
write 0x5a at head + 0xa5 at tail, read-back asserts both, free.
The head+tail write/read prevents DCE (failure path calls os.exit)
and proves a real page is backing the returned pointer.
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
// ostest — exercises lib/os surface that doesn't have a dedicated
|
||||
// test elsewhere. v1 covers [[os.getenv]] only, against an env state
|
||||
// pre-arranged by the C driver (test/wcc/974_getenv_run.c).
|
||||
// test elsewhere. Covers [[os.getenv]] (against env state pre-
|
||||
// arranged by test/wcc/974_getenv_run.c) and a direct
|
||||
// [[os.alloc]] / [[os.free]] roundtrip. memio's tests indirectly
|
||||
// cover alloc/free; the direct row here pins the FFI shape under
|
||||
// lib/os itself so future bindings refactors can't quietly drift.
|
||||
//
|
||||
// Convention follows the stdlib `_run` test fixtures: hand-rolled
|
||||
// @test fns dispatched from `main()` in numeric order, with a
|
||||
@@ -89,10 +92,31 @@ fn streq(a: str, b: str) bool = {
|
||||
};
|
||||
};
|
||||
|
||||
// ---- alloc/free: mmap-backed runtime allocator ----------------------
|
||||
//
|
||||
// Direct round-trip. memio's dynamic-buffer tests already exercise
|
||||
// os.alloc / os.free transitively; the row here pins the FFI shape
|
||||
// at the lib/os layer (write+read-back proves the returned page is
|
||||
// dereferenceable, not just non-nil).
|
||||
|
||||
@test fn test_alloc_free_roundtrip() void = {
|
||||
let p: *u8 = os.alloc(4096u64): *u8;
|
||||
if (p == nil: *u8) { fail(); };
|
||||
// Write a sentinel at the head and tail of the page, read it
|
||||
// back. A miscompiled binding (wrong arg order, wrong ABI, etc.)
|
||||
// would either fault or return zero here.
|
||||
p[0] = 90u8; // 0x5a
|
||||
p[4095] = 165u8; // 0xa5
|
||||
if (p[0] != 90u8) { fail(); };
|
||||
if (p[4095] != 165u8) { fail(); };
|
||||
os.free(p: *void, 4096u64);
|
||||
};
|
||||
|
||||
export fn main() i32 = {
|
||||
signalled = 1; test_getenv_set();
|
||||
signalled = 2; test_getenv_empty();
|
||||
signalled = 3; test_getenv_unset();
|
||||
signalled = 4; test_getenv_prefix_no_match();
|
||||
signalled = 5; test_alloc_free_roundtrip();
|
||||
return 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user