lib: collapse the manual rt_ensure append workarounds onto the fixed builtin (#34 follow-up)
shlex.appendstr, getopt.appendoption, bytes.appendslice and
strings.appendstr existed only because the append builtin stored the
first 8 bytes of the element; each carried its own @symbol("rt_ensure")
bind and a grow-then-store-through-*T body, with comments promising to
"collapse in one go when the append builtin is fixed". The previous
commit fixed the builtin; this removes all four helpers and their
rt_ensure binds and spells every call site as plain append().
Bonus correctness: getopt's appendoption passed a hardcoded membsz of
24, stale since the str 24B redesign made option {rune, str} 32B — the
manual growth under-allocated past 6 options while &opts.ptr[i] strode
32 (latent OOB). The builtin derives membsz from the type table
(probe: MOVQ $32, SI), closing that drift by construction.
This commit is contained in:
@@ -97,14 +97,6 @@ import encoding.utf8;
|
||||
import os;
|
||||
import strings;
|
||||
|
||||
// rt_ensure is the runtime slice-growth helper invoked by the
|
||||
// `append(s, v)` builtin. We bind it directly because the builtin's
|
||||
// expansion stores only 8 bytes of the new element (cgen emits a
|
||||
// single MOVQ), losing the `value: str` half of an [[option]].
|
||||
// [[appendoption]] grows manually and stores both fields via *option.
|
||||
// No public stdlib facade exposes rt_ensure, hence the direct @symbol.
|
||||
@symbol("rt_ensure") fn rtensure(s: *void, membsz: u64) void;
|
||||
|
||||
// helpkind — which slot of [[help]] is meaningful. Hare's getopt
|
||||
// also has a subcmd_help variant; not shipped here (see file header).
|
||||
export type helpkind = enum i32 {
|
||||
@@ -205,18 +197,6 @@ export type command = struct {
|
||||
helpcap: i32,
|
||||
};
|
||||
|
||||
// appendoption — grow `*opts` by one slot and store `(flag, value)`.
|
||||
// Avoids the `append(opts, pair)` builtin: cgen lowers that to a
|
||||
// MOVQ-of-the-first-8-bytes, which drops the `value: str` half.
|
||||
fn appendoption(opts: *[]option, flag: rune, value: str) void = {
|
||||
let newlen: i32 = opts.len + 1;
|
||||
opts.len = newlen;
|
||||
rtensure(opts: *void, 24u64);
|
||||
let dst: *option = &opts.ptr[newlen - 1];
|
||||
dst.flag = flag;
|
||||
dst.value = value;
|
||||
};
|
||||
|
||||
// findflag — lookup the kind of `r` in `hs`. Returns void if absent.
|
||||
//
|
||||
// Reads go through `&hs[i]` rather than `hs[i].field` directly: the
|
||||
@@ -307,7 +287,7 @@ export fn tryparse(out: *command, argv: []str, help: []help) (void | error) = {
|
||||
};
|
||||
case let k: helpkind => {
|
||||
if (k == helpkind.FLAG) {
|
||||
appendoption(&opts, r, "");
|
||||
append(opts, option { flag = r, value = "" });
|
||||
bi += 1;
|
||||
} else {
|
||||
// PARAM: glued value, or next argv slot.
|
||||
@@ -325,7 +305,7 @@ export fn tryparse(out: *command, argv: []str, help: []help) (void | error) = {
|
||||
case utf8.invalid =>
|
||||
abort("getopt: malformed argv UTF-8");
|
||||
};
|
||||
appendoption(&opts, r, v);
|
||||
append(opts, option { flag = r, value = v });
|
||||
advanced = true;
|
||||
} else {
|
||||
if (i + 1 >= argv.len) {
|
||||
@@ -340,7 +320,7 @@ export fn tryparse(out: *command, argv: []str, help: []help) (void | error) = {
|
||||
return e;
|
||||
};
|
||||
i += 1;
|
||||
appendoption(&opts, r, argv[i]);
|
||||
append(opts, option { flag = r, value = argv[i] });
|
||||
advanced = true;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user