// rt/ensure.ww — slice growth helper, archived into libwwrt.a. // // Companion to the `append(s, v)` builtin. The compiler lowers // `append(s, v)` to: // // ; push v // ; s.len += 1 // ; CALL rt_ensure(&s, sizeof(elem)) // ; ; ensure may have realloc'd, so re-read s.ptr // ; pop v // ; *(s.ptr + (s.len - 1) * elem_size) = v // // One helper handles every element width via the membsz parameter — // no per-type wrapper functions (appendu8 / appendi64) needed. // // User code never `use`s this — the symbol is resolved at link time // from libwwrt.a, like rt_malloc and rt_streq. rt/ensure.ww is compiled // standalone via `w6c rt/ensure.ww` (not through the driver). Under // strict-package (#24a) it declares `package rt;` (matching its // directory) like every primary section; the link-resolved name is // pinned bare via @symbol on the export (the clause would otherwise // mangle it to rt.rt_ensure). package rt; @symbol("rt_malloc") fn malloc(n: u64) *void; // Mirrors ww's []T header layout: 24 bytes with 8-byte slots. // ww's source uses i32 for len/cap but the compiler stores them in // 8-byte slots; declaring as i64 here keeps the field offsets right // for this polymorphic alias. type slice = struct { ptr: *u8, len: i64, cap: i64, }; @symbol("rt_ensure") export fn rt_ensure(s: *slice, membsz: u64) void = { if (s.cap >= s.len) { return; }; let nc: i64 = s.cap * 2i64; if (nc < 8i64) { nc = 8i64; }; for (nc < s.len) { nc *= 2i64; }; // Task #30 (commit 3) upgrades to nullable *void + null-check. // For now, rt_malloc returns plain *void; OOM faults on deref. let np: *u8 = malloc((nc: u64) * membsz): *u8; let n: u64 = (s.cap: u64) * membsz; let i: u64 = 0u64; for (i < n) { np[i] = s.ptr[i]; i += 1u64; }; // No free: the bump allocator (rt/malloc.ww) can't reclaim a // mid-chunk region; the old buffer leaks until process exit. (#8) s.ptr = np; s.cap = nc; };