// 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_alloc and rt_streq. No `module` declaration: // rt/ensure.ww is compiled standalone via `w6c rt/ensure.ww` (not // through the driver), and its `export fn rt_ensure` must keep its // bare symbol name so the linker resolves it. @symbol("rt_alloc") fn alloc(n: u64) *void; @symbol("rt_free") fn free(p: *void, 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, }; 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: the alloc builtin returns `(*T | nomem)`; `!` aborts // on OOM. The longstanding sizeof-only allocation bug (task #27) // stays unfixed here — rt_ensure presumes a same-module `fn // alloc(n)` resolution that the bare cur_mod=NULL gate at // cmd/wcc/check.c:984 doesn't honour. let np: *u8 = alloc((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; }; if (s.cap > 0i64) { free(s.ptr: *void, (s.cap: u64) * membsz); }; s.ptr = np; s.cap = nc; };