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:
@@ -386,28 +386,6 @@ export fn remaining_tokens(s: *tokenizer) []u8 = {
|
|||||||
return s.in;
|
return s.in;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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 .len/.cap fields of a []u8 element (24B).
|
|
||||||
// Mirrors the same workaround in lib/shlex.shlex (appendstr, 16B) and
|
|
||||||
// lib/getopt.getopt (appendoption, 24B); collapses in one go when the
|
|
||||||
// append builtin learns to store the full element width.
|
|
||||||
@symbol("rt_ensure") fn rtensure(s: *void, membsz: u64) void;
|
|
||||||
|
|
||||||
// appendslice — grow `*slice` by one and store `item` (24B). Mirror
|
|
||||||
// of [[shlex.appendstr]] / [[getopt.appendoption]]. Bypasses the
|
|
||||||
// `append` builtin's first-8B-only-store gap for a slice-element.
|
|
||||||
fn appendslice(slice: *[][]u8, item: []u8) void = {
|
|
||||||
let newlen: i32 = slice.len + 1;
|
|
||||||
slice.len = newlen;
|
|
||||||
rtensure(slice: *void, 24u64);
|
|
||||||
let dst: *[]u8 = &slice.ptr[newlen - 1];
|
|
||||||
dst.ptr = item.ptr;
|
|
||||||
dst.len = item.len;
|
|
||||||
dst.cap = item.cap;
|
|
||||||
};
|
|
||||||
|
|
||||||
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
||||||
// tokens via forward iteration. The trailing slot (when more than
|
// tokens via forward iteration. The trailing slot (when more than
|
||||||
// `n - 1` tokens exist) holds the unconsumed remainder.
|
// `n - 1` tokens exist) holds the unconsumed remainder.
|
||||||
@@ -432,7 +410,7 @@ export fn splitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: []u8 => { appendslice(&toks, s); };
|
case let s: []u8 => { append(toks, s); };
|
||||||
case done => { return toks; };
|
case done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -441,7 +419,7 @@ export fn splitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
case done => void;
|
case done => void;
|
||||||
case let pk: []u8 => {
|
case let pk: []u8 => {
|
||||||
let r: []u8 = remaining_tokens(&tok);
|
let r: []u8 = remaining_tokens(&tok);
|
||||||
appendslice(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return toks;
|
return toks;
|
||||||
@@ -470,7 +448,7 @@ export fn rsplitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: []u8 => { appendslice(&toks, s); };
|
case let s: []u8 => { append(toks, s); };
|
||||||
case done => { return toks; };
|
case done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -479,7 +457,7 @@ export fn rsplitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
case done => void;
|
case done => void;
|
||||||
case let pk: []u8 => {
|
case let pk: []u8 => {
|
||||||
let r: []u8 = remaining_tokens(&tok);
|
let r: []u8 = remaining_tokens(&tok);
|
||||||
appendslice(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -97,14 +97,6 @@ import encoding.utf8;
|
|||||||
import os;
|
import os;
|
||||||
import strings;
|
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
|
// helpkind — which slot of [[help]] is meaningful. Hare's getopt
|
||||||
// also has a subcmd_help variant; not shipped here (see file header).
|
// also has a subcmd_help variant; not shipped here (see file header).
|
||||||
export type helpkind = enum i32 {
|
export type helpkind = enum i32 {
|
||||||
@@ -205,18 +197,6 @@ export type command = struct {
|
|||||||
helpcap: i32,
|
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.
|
// findflag — lookup the kind of `r` in `hs`. Returns void if absent.
|
||||||
//
|
//
|
||||||
// Reads go through `&hs[i]` rather than `hs[i].field` directly: the
|
// 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 => {
|
case let k: helpkind => {
|
||||||
if (k == helpkind.FLAG) {
|
if (k == helpkind.FLAG) {
|
||||||
appendoption(&opts, r, "");
|
append(opts, option { flag = r, value = "" });
|
||||||
bi += 1;
|
bi += 1;
|
||||||
} else {
|
} else {
|
||||||
// PARAM: glued value, or next argv slot.
|
// 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 =>
|
case utf8.invalid =>
|
||||||
abort("getopt: malformed argv UTF-8");
|
abort("getopt: malformed argv UTF-8");
|
||||||
};
|
};
|
||||||
appendoption(&opts, r, v);
|
append(opts, option { flag = r, value = v });
|
||||||
advanced = true;
|
advanced = true;
|
||||||
} else {
|
} else {
|
||||||
if (i + 1 >= argv.len) {
|
if (i + 1 >= argv.len) {
|
||||||
@@ -340,7 +320,7 @@ export fn tryparse(out: *command, argv: []str, help: []help) (void | error) = {
|
|||||||
return e;
|
return e;
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
appendoption(&opts, r, argv[i]);
|
append(opts, option { flag = r, value = argv[i] });
|
||||||
advanced = true;
|
advanced = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
//
|
//
|
||||||
// - Drop nomem: ww os.alloc has no recoverable failure path (OOM
|
// - Drop nomem: ww os.alloc has no recoverable failure path (OOM
|
||||||
// yields a poisonous pointer that faults on deref; see lib/os.ww
|
// yields a poisonous pointer that faults on deref; see lib/os.ww
|
||||||
// comment). Same precedent as strings.dup, getopt.appendoption.
|
// comment). Same precedent as strings.dup.
|
||||||
// Hare's (...|syntaxerr|nomem) collapses to (...|syntaxerr).
|
// Hare's (...|syntaxerr|nomem) collapses to (...|syntaxerr).
|
||||||
//
|
//
|
||||||
// - Byte-wise iteration via i32 cursor instead of Hare's
|
// - Byte-wise iteration via i32 cursor instead of Hare's
|
||||||
@@ -55,15 +55,6 @@
|
|||||||
// "write one byte" path goes through `io.write(s, buf[0:1])`
|
// "write one byte" path goes through `io.write(s, buf[0:1])`
|
||||||
// against a stack `[N]u8`. Same shape as fmt's rune-arm.
|
// against a stack `[N]u8`. Same shape as fmt's rune-arm.
|
||||||
//
|
//
|
||||||
// - `[]str` grown via direct rt_ensure rather than the `append`
|
|
||||||
// builtin: cgen lowers `append(slice, element)` to a single MOVQ
|
|
||||||
// of the first 8 bytes, which drops the `len` half of a 16B str
|
|
||||||
// element. [[appendstr]] writes both halves through `*str`.
|
|
||||||
// Same gap, same workaround, same comment shape as
|
|
||||||
// [[getopt.appendoption]] (lib/getopt/getopt.ww:96-106) — kept
|
|
||||||
// greppable across modules. When the append-builtin is fixed,
|
|
||||||
// all three (getopt, shlex, anywhere else) collapse in one go.
|
|
||||||
//
|
|
||||||
// - Internal [[dupstr]] inlined rather than `use strings;`: keeps
|
// - Internal [[dupstr]] inlined rather than `use strings;`: keeps
|
||||||
// the dep surface small for a 6-line helper. shlex doesn't reach
|
// the dep surface small for a 6-line helper. shlex doesn't reach
|
||||||
// for any other strings:: routine, and callers free split()'s
|
// for any other strings:: routine, and callers free split()'s
|
||||||
@@ -94,13 +85,6 @@ import memio;
|
|||||||
import os;
|
import os;
|
||||||
import strings;
|
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 `len` half of a `str` (16B). No public
|
|
||||||
// stdlib facade exposes it, hence the direct @symbol.
|
|
||||||
@symbol("rt_ensure") fn rtensure(s: *void, membsz: u64) void;
|
|
||||||
|
|
||||||
// syntaxerr — the input wasn't a valid shell-tokenizable string
|
// syntaxerr — the input wasn't a valid shell-tokenizable string
|
||||||
// (unterminated quote / bare trailing backslash). Mirrors Hare's
|
// (unterminated quote / bare trailing backslash). Mirrors Hare's
|
||||||
// shlex::syntaxerr; a `!void` so it can ride a tagged-union return
|
// shlex::syntaxerr; a `!void` so it can ride a tagged-union return
|
||||||
@@ -127,18 +111,6 @@ fn dupstr(s: str) str = {
|
|||||||
return strings.frombytes(buf);
|
return strings.frombytes(buf);
|
||||||
};
|
};
|
||||||
|
|
||||||
// appendstr — grow `*slice` by one and store `item` (16B). Bypasses
|
|
||||||
// the `append` builtin's first-8B-only store gap; mirror of
|
|
||||||
// [[getopt.appendoption]] for the `[]option` case.
|
|
||||||
fn appendstr(slice: *[]str, item: str) void = {
|
|
||||||
let newlen: i32 = slice.len + 1;
|
|
||||||
slice.len = newlen;
|
|
||||||
rtensure(slice: *void, size(str): u64);
|
|
||||||
let dst: *str = &slice.ptr[newlen - 1];
|
|
||||||
dst.ptr = item.ptr;
|
|
||||||
dst.len = item.len;
|
|
||||||
};
|
|
||||||
|
|
||||||
// freepartial — drop a partially built [[split]] result on the
|
// freepartial — drop a partially built [[split]] result on the
|
||||||
// syntaxerr path. Hare uses a `defer if (!ok)` guard; ww has no defer,
|
// syntaxerr path. Hare uses a `defer if (!ok)` guard; ww has no defer,
|
||||||
// so we hand-roll the cleanup at every error return. Mirror of
|
// so we hand-roll the cleanup at every error return. Mirror of
|
||||||
@@ -271,7 +243,7 @@ export fn split(in: str) ([]str | syntaxerr) = {
|
|||||||
if (!first) {
|
if (!first) {
|
||||||
let view: str = memio.string(&st);
|
let view: str = memio.string(&st);
|
||||||
let owned: str = dupstr(view);
|
let owned: str = dupstr(view);
|
||||||
appendstr(&slice, owned);
|
append(slice, owned);
|
||||||
memio.reset(&st);
|
memio.reset(&st);
|
||||||
};
|
};
|
||||||
dirty = false;
|
dirty = false;
|
||||||
@@ -315,7 +287,7 @@ export fn split(in: str) ([]str | syntaxerr) = {
|
|||||||
if (dirty) {
|
if (dirty) {
|
||||||
let view: str = memio.string(&st);
|
let view: str = memio.string(&st);
|
||||||
let owned: str = dupstr(view);
|
let owned: str = dupstr(view);
|
||||||
appendstr(&slice, owned);
|
append(slice, owned);
|
||||||
};
|
};
|
||||||
|
|
||||||
let _c = io.close(snk);
|
let _c = io.close(snk);
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export fn dup(s: str) str = {
|
|||||||
// so the only nomem propagation point is the initial slice alloc.
|
// so the only nomem propagation point is the initial slice alloc.
|
||||||
// With no inner failure path, the rollback is structurally a no-op
|
// With no inner failure path, the rollback is structurally a no-op
|
||||||
// and is omitted; it returns once dup graduates to `(str | nomem)`
|
// and is omitted; it returns once dup graduates to `(str | nomem)`
|
||||||
// (#46). The pre-allocated slice has `cap == s.len`, so appendstr's
|
// (#46). The pre-allocated slice has `cap == s.len`, so append's
|
||||||
// rt_ensure call never reaches the grow branch.
|
// rt_ensure call never reaches the grow branch.
|
||||||
//
|
//
|
||||||
// Empty input bypasses the alloc: rt_malloc(0) is an mmap of 0 bytes
|
// Empty input bypasses the alloc: rt_malloc(0) is an mmap of 0 bytes
|
||||||
@@ -107,7 +107,7 @@ export fn dupall(s: []str) ([]str | nomem) = {
|
|||||||
let newsl: []str = alloc([], s.len)?;
|
let newsl: []str = alloc([], s.len)?;
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < s.len) {
|
for (i < s.len) {
|
||||||
appendstr(&newsl, dup(s[i]));
|
append(newsl, dup(s[i]));
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
return newsl;
|
return newsl;
|
||||||
@@ -733,24 +733,6 @@ export fn rcut(in: str, delim: str) (str, str) = {
|
|||||||
return (frombytes(a), frombytes(b));
|
return (frombytes(a), frombytes(b));
|
||||||
};
|
};
|
||||||
|
|
||||||
// rt_ensure is the runtime slice-growth helper invoked by the
|
|
||||||
// `append(s, v)` builtin. Direct bind for the same reason as
|
|
||||||
// lib/shlex.shlex (appendstr, 16B): the builtin's expansion stores
|
|
||||||
// only 8B of the new element, losing the `.len` half of a `str`.
|
|
||||||
@symbol("rt_ensure") fn rtensure(s: *void, membsz: u64) void;
|
|
||||||
|
|
||||||
// appendstr — grow `*slice` by one and store `item` (16B). Mirror of
|
|
||||||
// lib/shlex.shlex appendstr. Collapses when the append builtin learns
|
|
||||||
// to store the full element width.
|
|
||||||
fn appendstr(slice: *[]str, item: str) void = {
|
|
||||||
let newlen: i32 = slice.len + 1;
|
|
||||||
slice.len = newlen;
|
|
||||||
rtensure(slice: *void, size(str): u64);
|
|
||||||
let dst: *str = &slice.ptr[newlen - 1];
|
|
||||||
dst.ptr = item.ptr;
|
|
||||||
dst.len = item.len;
|
|
||||||
};
|
|
||||||
|
|
||||||
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
||||||
// tokens via forward iteration. The trailing slot (when more than
|
// tokens via forward iteration. The trailing slot (when more than
|
||||||
// `n - 1` tokens exist) holds the unconsumed remainder. Strings
|
// `n - 1` tokens exist) holds the unconsumed remainder. Strings
|
||||||
@@ -773,7 +755,7 @@ export fn splitn(in: str, delim: str, n: i32) []str = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: str => { appendstr(&toks, s); };
|
case let s: str => { append(toks, s); };
|
||||||
case bytes.done => { return toks; };
|
case bytes.done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -782,7 +764,7 @@ export fn splitn(in: str, delim: str, n: i32) []str = {
|
|||||||
case bytes.done => void;
|
case bytes.done => void;
|
||||||
case let pk: str => {
|
case let pk: str => {
|
||||||
let r: str = remaining_tokens(&tok);
|
let r: str = remaining_tokens(&tok);
|
||||||
appendstr(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return toks;
|
return toks;
|
||||||
@@ -807,7 +789,7 @@ export fn rsplitn(in: str, delim: str, n: i32) []str = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: str => { appendstr(&toks, s); };
|
case let s: str => { append(toks, s); };
|
||||||
case bytes.done => { return toks; };
|
case bytes.done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -816,7 +798,7 @@ export fn rsplitn(in: str, delim: str, n: i32) []str = {
|
|||||||
case bytes.done => void;
|
case bytes.done => void;
|
||||||
case let pk: str => {
|
case let pk: str => {
|
||||||
let r: str = remaining_tokens(&tok);
|
let r: str = remaining_tokens(&tok);
|
||||||
appendstr(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1244,28 +1244,6 @@ export fn remaining_tokens(s: *tokenizer) []u8 = {
|
|||||||
return s.in;
|
return s.in;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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 .len/.cap fields of a []u8 element (24B).
|
|
||||||
// Mirrors the same workaround in lib/shlex.shlex (appendstr, 16B) and
|
|
||||||
// lib/getopt.getopt (appendoption, 24B); collapses in one go when the
|
|
||||||
// append builtin learns to store the full element width.
|
|
||||||
@symbol("rt_ensure") fn rtensure(s: *void, membsz: u64) void;
|
|
||||||
|
|
||||||
// appendslice — grow `*slice` by one and store `item` (24B). Mirror
|
|
||||||
// of [[shlex.appendstr]] / [[getopt.appendoption]]. Bypasses the
|
|
||||||
// `append` builtin's first-8B-only-store gap for a slice-element.
|
|
||||||
fn appendslice(slice: *[][]u8, item: []u8) void = {
|
|
||||||
let newlen: i32 = slice.len + 1;
|
|
||||||
slice.len = newlen;
|
|
||||||
rtensure(slice: *void, 24u64);
|
|
||||||
let dst: *[]u8 = &slice.ptr[newlen - 1];
|
|
||||||
dst.ptr = item.ptr;
|
|
||||||
dst.len = item.len;
|
|
||||||
dst.cap = item.cap;
|
|
||||||
};
|
|
||||||
|
|
||||||
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
||||||
// tokens via forward iteration. The trailing slot (when more than
|
// tokens via forward iteration. The trailing slot (when more than
|
||||||
// `n - 1` tokens exist) holds the unconsumed remainder.
|
// `n - 1` tokens exist) holds the unconsumed remainder.
|
||||||
@@ -1290,7 +1268,7 @@ export fn splitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: []u8 => { appendslice(&toks, s); };
|
case let s: []u8 => { append(toks, s); };
|
||||||
case done => { return toks; };
|
case done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -1299,7 +1277,7 @@ export fn splitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
case done => void;
|
case done => void;
|
||||||
case let pk: []u8 => {
|
case let pk: []u8 => {
|
||||||
let r: []u8 = remaining_tokens(&tok);
|
let r: []u8 = remaining_tokens(&tok);
|
||||||
appendslice(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return toks;
|
return toks;
|
||||||
@@ -1328,7 +1306,7 @@ export fn rsplitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: []u8 => { appendslice(&toks, s); };
|
case let s: []u8 => { append(toks, s); };
|
||||||
case done => { return toks; };
|
case done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -1337,7 +1315,7 @@ export fn rsplitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
case done => void;
|
case done => void;
|
||||||
case let pk: []u8 => {
|
case let pk: []u8 => {
|
||||||
let r: []u8 = remaining_tokens(&tok);
|
let r: []u8 = remaining_tokens(&tok);
|
||||||
appendslice(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1955,7 +1933,7 @@ export fn dup(s: str) str = {
|
|||||||
// so the only nomem propagation point is the initial slice alloc.
|
// so the only nomem propagation point is the initial slice alloc.
|
||||||
// With no inner failure path, the rollback is structurally a no-op
|
// With no inner failure path, the rollback is structurally a no-op
|
||||||
// and is omitted; it returns once dup graduates to `(str | nomem)`
|
// and is omitted; it returns once dup graduates to `(str | nomem)`
|
||||||
// (#46). The pre-allocated slice has `cap == s.len`, so appendstr's
|
// (#46). The pre-allocated slice has `cap == s.len`, so append's
|
||||||
// rt_ensure call never reaches the grow branch.
|
// rt_ensure call never reaches the grow branch.
|
||||||
//
|
//
|
||||||
// Empty input bypasses the alloc: rt_malloc(0) is an mmap of 0 bytes
|
// Empty input bypasses the alloc: rt_malloc(0) is an mmap of 0 bytes
|
||||||
@@ -1974,7 +1952,7 @@ export fn dupall(s: []str) ([]str | nomem) = {
|
|||||||
let newsl: []str = alloc([], s.len)?;
|
let newsl: []str = alloc([], s.len)?;
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < s.len) {
|
for (i < s.len) {
|
||||||
appendstr(&newsl, dup(s[i]));
|
append(newsl, dup(s[i]));
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
return newsl;
|
return newsl;
|
||||||
@@ -2600,24 +2578,6 @@ export fn rcut(in: str, delim: str) (str, str) = {
|
|||||||
return (frombytes(a), frombytes(b));
|
return (frombytes(a), frombytes(b));
|
||||||
};
|
};
|
||||||
|
|
||||||
// rt_ensure is the runtime slice-growth helper invoked by the
|
|
||||||
// `append(s, v)` builtin. Direct bind for the same reason as
|
|
||||||
// lib/shlex.shlex (appendstr, 16B): the builtin's expansion stores
|
|
||||||
// only 8B of the new element, losing the `.len` half of a `str`.
|
|
||||||
@symbol("rt_ensure") fn rtensure(s: *void, membsz: u64) void;
|
|
||||||
|
|
||||||
// appendstr — grow `*slice` by one and store `item` (16B). Mirror of
|
|
||||||
// lib/shlex.shlex appendstr. Collapses when the append builtin learns
|
|
||||||
// to store the full element width.
|
|
||||||
fn appendstr(slice: *[]str, item: str) void = {
|
|
||||||
let newlen: i32 = slice.len + 1;
|
|
||||||
slice.len = newlen;
|
|
||||||
rtensure(slice: *void, size(str): u64);
|
|
||||||
let dst: *str = &slice.ptr[newlen - 1];
|
|
||||||
dst.ptr = item.ptr;
|
|
||||||
dst.len = item.len;
|
|
||||||
};
|
|
||||||
|
|
||||||
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
||||||
// tokens via forward iteration. The trailing slot (when more than
|
// tokens via forward iteration. The trailing slot (when more than
|
||||||
// `n - 1` tokens exist) holds the unconsumed remainder. Strings
|
// `n - 1` tokens exist) holds the unconsumed remainder. Strings
|
||||||
@@ -2640,7 +2600,7 @@ export fn splitn(in: str, delim: str, n: i32) []str = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: str => { appendstr(&toks, s); };
|
case let s: str => { append(toks, s); };
|
||||||
case bytes.done => { return toks; };
|
case bytes.done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -2649,7 +2609,7 @@ export fn splitn(in: str, delim: str, n: i32) []str = {
|
|||||||
case bytes.done => void;
|
case bytes.done => void;
|
||||||
case let pk: str => {
|
case let pk: str => {
|
||||||
let r: str = remaining_tokens(&tok);
|
let r: str = remaining_tokens(&tok);
|
||||||
appendstr(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return toks;
|
return toks;
|
||||||
@@ -2674,7 +2634,7 @@ export fn rsplitn(in: str, delim: str, n: i32) []str = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: str => { appendstr(&toks, s); };
|
case let s: str => { append(toks, s); };
|
||||||
case bytes.done => { return toks; };
|
case bytes.done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -2683,7 +2643,7 @@ export fn rsplitn(in: str, delim: str, n: i32) []str = {
|
|||||||
case bytes.done => void;
|
case bytes.done => void;
|
||||||
case let pk: str => {
|
case let pk: str => {
|
||||||
let r: str = remaining_tokens(&tok);
|
let r: str = remaining_tokens(&tok);
|
||||||
appendstr(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1244,28 +1244,6 @@ export fn remaining_tokens(s: *tokenizer) []u8 = {
|
|||||||
return s.in;
|
return s.in;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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 .len/.cap fields of a []u8 element (24B).
|
|
||||||
// Mirrors the same workaround in lib/shlex.shlex (appendstr, 16B) and
|
|
||||||
// lib/getopt.getopt (appendoption, 24B); collapses in one go when the
|
|
||||||
// append builtin learns to store the full element width.
|
|
||||||
@symbol("rt_ensure") fn rtensure(s: *void, membsz: u64) void;
|
|
||||||
|
|
||||||
// appendslice — grow `*slice` by one and store `item` (24B). Mirror
|
|
||||||
// of [[shlex.appendstr]] / [[getopt.appendoption]]. Bypasses the
|
|
||||||
// `append` builtin's first-8B-only-store gap for a slice-element.
|
|
||||||
fn appendslice(slice: *[][]u8, item: []u8) void = {
|
|
||||||
let newlen: i32 = slice.len + 1;
|
|
||||||
slice.len = newlen;
|
|
||||||
rtensure(slice: *void, 24u64);
|
|
||||||
let dst: *[]u8 = &slice.ptr[newlen - 1];
|
|
||||||
dst.ptr = item.ptr;
|
|
||||||
dst.len = item.len;
|
|
||||||
dst.cap = item.cap;
|
|
||||||
};
|
|
||||||
|
|
||||||
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
||||||
// tokens via forward iteration. The trailing slot (when more than
|
// tokens via forward iteration. The trailing slot (when more than
|
||||||
// `n - 1` tokens exist) holds the unconsumed remainder.
|
// `n - 1` tokens exist) holds the unconsumed remainder.
|
||||||
@@ -1290,7 +1268,7 @@ export fn splitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: []u8 => { appendslice(&toks, s); };
|
case let s: []u8 => { append(toks, s); };
|
||||||
case done => { return toks; };
|
case done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -1299,7 +1277,7 @@ export fn splitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
case done => void;
|
case done => void;
|
||||||
case let pk: []u8 => {
|
case let pk: []u8 => {
|
||||||
let r: []u8 = remaining_tokens(&tok);
|
let r: []u8 = remaining_tokens(&tok);
|
||||||
appendslice(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return toks;
|
return toks;
|
||||||
@@ -1328,7 +1306,7 @@ export fn rsplitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: []u8 => { appendslice(&toks, s); };
|
case let s: []u8 => { append(toks, s); };
|
||||||
case done => { return toks; };
|
case done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -1337,7 +1315,7 @@ export fn rsplitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
case done => void;
|
case done => void;
|
||||||
case let pk: []u8 => {
|
case let pk: []u8 => {
|
||||||
let r: []u8 = remaining_tokens(&tok);
|
let r: []u8 = remaining_tokens(&tok);
|
||||||
appendslice(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1955,7 +1933,7 @@ export fn dup(s: str) str = {
|
|||||||
// so the only nomem propagation point is the initial slice alloc.
|
// so the only nomem propagation point is the initial slice alloc.
|
||||||
// With no inner failure path, the rollback is structurally a no-op
|
// With no inner failure path, the rollback is structurally a no-op
|
||||||
// and is omitted; it returns once dup graduates to `(str | nomem)`
|
// and is omitted; it returns once dup graduates to `(str | nomem)`
|
||||||
// (#46). The pre-allocated slice has `cap == s.len`, so appendstr's
|
// (#46). The pre-allocated slice has `cap == s.len`, so append's
|
||||||
// rt_ensure call never reaches the grow branch.
|
// rt_ensure call never reaches the grow branch.
|
||||||
//
|
//
|
||||||
// Empty input bypasses the alloc: rt_malloc(0) is an mmap of 0 bytes
|
// Empty input bypasses the alloc: rt_malloc(0) is an mmap of 0 bytes
|
||||||
@@ -1974,7 +1952,7 @@ export fn dupall(s: []str) ([]str | nomem) = {
|
|||||||
let newsl: []str = alloc([], s.len)?;
|
let newsl: []str = alloc([], s.len)?;
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < s.len) {
|
for (i < s.len) {
|
||||||
appendstr(&newsl, dup(s[i]));
|
append(newsl, dup(s[i]));
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
return newsl;
|
return newsl;
|
||||||
@@ -2600,24 +2578,6 @@ export fn rcut(in: str, delim: str) (str, str) = {
|
|||||||
return (frombytes(a), frombytes(b));
|
return (frombytes(a), frombytes(b));
|
||||||
};
|
};
|
||||||
|
|
||||||
// rt_ensure is the runtime slice-growth helper invoked by the
|
|
||||||
// `append(s, v)` builtin. Direct bind for the same reason as
|
|
||||||
// lib/shlex.shlex (appendstr, 16B): the builtin's expansion stores
|
|
||||||
// only 8B of the new element, losing the `.len` half of a `str`.
|
|
||||||
@symbol("rt_ensure") fn rtensure(s: *void, membsz: u64) void;
|
|
||||||
|
|
||||||
// appendstr — grow `*slice` by one and store `item` (16B). Mirror of
|
|
||||||
// lib/shlex.shlex appendstr. Collapses when the append builtin learns
|
|
||||||
// to store the full element width.
|
|
||||||
fn appendstr(slice: *[]str, item: str) void = {
|
|
||||||
let newlen: i32 = slice.len + 1;
|
|
||||||
slice.len = newlen;
|
|
||||||
rtensure(slice: *void, size(str): u64);
|
|
||||||
let dst: *str = &slice.ptr[newlen - 1];
|
|
||||||
dst.ptr = item.ptr;
|
|
||||||
dst.len = item.len;
|
|
||||||
};
|
|
||||||
|
|
||||||
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
||||||
// tokens via forward iteration. The trailing slot (when more than
|
// tokens via forward iteration. The trailing slot (when more than
|
||||||
// `n - 1` tokens exist) holds the unconsumed remainder. Strings
|
// `n - 1` tokens exist) holds the unconsumed remainder. Strings
|
||||||
@@ -2640,7 +2600,7 @@ export fn splitn(in: str, delim: str, n: i32) []str = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: str => { appendstr(&toks, s); };
|
case let s: str => { append(toks, s); };
|
||||||
case bytes.done => { return toks; };
|
case bytes.done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -2649,7 +2609,7 @@ export fn splitn(in: str, delim: str, n: i32) []str = {
|
|||||||
case bytes.done => void;
|
case bytes.done => void;
|
||||||
case let pk: str => {
|
case let pk: str => {
|
||||||
let r: str = remaining_tokens(&tok);
|
let r: str = remaining_tokens(&tok);
|
||||||
appendstr(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return toks;
|
return toks;
|
||||||
@@ -2674,7 +2634,7 @@ export fn rsplitn(in: str, delim: str, n: i32) []str = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: str => { appendstr(&toks, s); };
|
case let s: str => { append(toks, s); };
|
||||||
case bytes.done => { return toks; };
|
case bytes.done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -2683,7 +2643,7 @@ export fn rsplitn(in: str, delim: str, n: i32) []str = {
|
|||||||
case bytes.done => void;
|
case bytes.done => void;
|
||||||
case let pk: str => {
|
case let pk: str => {
|
||||||
let r: str = remaining_tokens(&tok);
|
let r: str = remaining_tokens(&tok);
|
||||||
appendstr(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1244,28 +1244,6 @@ export fn remaining_tokens(s: *tokenizer) []u8 = {
|
|||||||
return s.in;
|
return s.in;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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 .len/.cap fields of a []u8 element (24B).
|
|
||||||
// Mirrors the same workaround in lib/shlex.shlex (appendstr, 16B) and
|
|
||||||
// lib/getopt.getopt (appendoption, 24B); collapses in one go when the
|
|
||||||
// append builtin learns to store the full element width.
|
|
||||||
@symbol("rt_ensure") fn rtensure(s: *void, membsz: u64) void;
|
|
||||||
|
|
||||||
// appendslice — grow `*slice` by one and store `item` (24B). Mirror
|
|
||||||
// of [[shlex.appendstr]] / [[getopt.appendoption]]. Bypasses the
|
|
||||||
// `append` builtin's first-8B-only-store gap for a slice-element.
|
|
||||||
fn appendslice(slice: *[][]u8, item: []u8) void = {
|
|
||||||
let newlen: i32 = slice.len + 1;
|
|
||||||
slice.len = newlen;
|
|
||||||
rtensure(slice: *void, 24u64);
|
|
||||||
let dst: *[]u8 = &slice.ptr[newlen - 1];
|
|
||||||
dst.ptr = item.ptr;
|
|
||||||
dst.len = item.len;
|
|
||||||
dst.cap = item.cap;
|
|
||||||
};
|
|
||||||
|
|
||||||
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
||||||
// tokens via forward iteration. The trailing slot (when more than
|
// tokens via forward iteration. The trailing slot (when more than
|
||||||
// `n - 1` tokens exist) holds the unconsumed remainder.
|
// `n - 1` tokens exist) holds the unconsumed remainder.
|
||||||
@@ -1290,7 +1268,7 @@ export fn splitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: []u8 => { appendslice(&toks, s); };
|
case let s: []u8 => { append(toks, s); };
|
||||||
case done => { return toks; };
|
case done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -1299,7 +1277,7 @@ export fn splitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
case done => void;
|
case done => void;
|
||||||
case let pk: []u8 => {
|
case let pk: []u8 => {
|
||||||
let r: []u8 = remaining_tokens(&tok);
|
let r: []u8 = remaining_tokens(&tok);
|
||||||
appendslice(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return toks;
|
return toks;
|
||||||
@@ -1328,7 +1306,7 @@ export fn rsplitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: []u8 => { appendslice(&toks, s); };
|
case let s: []u8 => { append(toks, s); };
|
||||||
case done => { return toks; };
|
case done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -1337,7 +1315,7 @@ export fn rsplitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
case done => void;
|
case done => void;
|
||||||
case let pk: []u8 => {
|
case let pk: []u8 => {
|
||||||
let r: []u8 = remaining_tokens(&tok);
|
let r: []u8 = remaining_tokens(&tok);
|
||||||
appendslice(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1955,7 +1933,7 @@ export fn dup(s: str) str = {
|
|||||||
// so the only nomem propagation point is the initial slice alloc.
|
// so the only nomem propagation point is the initial slice alloc.
|
||||||
// With no inner failure path, the rollback is structurally a no-op
|
// With no inner failure path, the rollback is structurally a no-op
|
||||||
// and is omitted; it returns once dup graduates to `(str | nomem)`
|
// and is omitted; it returns once dup graduates to `(str | nomem)`
|
||||||
// (#46). The pre-allocated slice has `cap == s.len`, so appendstr's
|
// (#46). The pre-allocated slice has `cap == s.len`, so append's
|
||||||
// rt_ensure call never reaches the grow branch.
|
// rt_ensure call never reaches the grow branch.
|
||||||
//
|
//
|
||||||
// Empty input bypasses the alloc: rt_malloc(0) is an mmap of 0 bytes
|
// Empty input bypasses the alloc: rt_malloc(0) is an mmap of 0 bytes
|
||||||
@@ -1974,7 +1952,7 @@ export fn dupall(s: []str) ([]str | nomem) = {
|
|||||||
let newsl: []str = alloc([], s.len)?;
|
let newsl: []str = alloc([], s.len)?;
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < s.len) {
|
for (i < s.len) {
|
||||||
appendstr(&newsl, dup(s[i]));
|
append(newsl, dup(s[i]));
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
return newsl;
|
return newsl;
|
||||||
@@ -2600,24 +2578,6 @@ export fn rcut(in: str, delim: str) (str, str) = {
|
|||||||
return (frombytes(a), frombytes(b));
|
return (frombytes(a), frombytes(b));
|
||||||
};
|
};
|
||||||
|
|
||||||
// rt_ensure is the runtime slice-growth helper invoked by the
|
|
||||||
// `append(s, v)` builtin. Direct bind for the same reason as
|
|
||||||
// lib/shlex.shlex (appendstr, 16B): the builtin's expansion stores
|
|
||||||
// only 8B of the new element, losing the `.len` half of a `str`.
|
|
||||||
@symbol("rt_ensure") fn rtensure(s: *void, membsz: u64) void;
|
|
||||||
|
|
||||||
// appendstr — grow `*slice` by one and store `item` (16B). Mirror of
|
|
||||||
// lib/shlex.shlex appendstr. Collapses when the append builtin learns
|
|
||||||
// to store the full element width.
|
|
||||||
fn appendstr(slice: *[]str, item: str) void = {
|
|
||||||
let newlen: i32 = slice.len + 1;
|
|
||||||
slice.len = newlen;
|
|
||||||
rtensure(slice: *void, size(str): u64);
|
|
||||||
let dst: *str = &slice.ptr[newlen - 1];
|
|
||||||
dst.ptr = item.ptr;
|
|
||||||
dst.len = item.len;
|
|
||||||
};
|
|
||||||
|
|
||||||
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
||||||
// tokens via forward iteration. The trailing slot (when more than
|
// tokens via forward iteration. The trailing slot (when more than
|
||||||
// `n - 1` tokens exist) holds the unconsumed remainder. Strings
|
// `n - 1` tokens exist) holds the unconsumed remainder. Strings
|
||||||
@@ -2640,7 +2600,7 @@ export fn splitn(in: str, delim: str, n: i32) []str = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: str => { appendstr(&toks, s); };
|
case let s: str => { append(toks, s); };
|
||||||
case bytes.done => { return toks; };
|
case bytes.done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -2649,7 +2609,7 @@ export fn splitn(in: str, delim: str, n: i32) []str = {
|
|||||||
case bytes.done => void;
|
case bytes.done => void;
|
||||||
case let pk: str => {
|
case let pk: str => {
|
||||||
let r: str = remaining_tokens(&tok);
|
let r: str = remaining_tokens(&tok);
|
||||||
appendstr(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return toks;
|
return toks;
|
||||||
@@ -2674,7 +2634,7 @@ export fn rsplitn(in: str, delim: str, n: i32) []str = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: str => { appendstr(&toks, s); };
|
case let s: str => { append(toks, s); };
|
||||||
case bytes.done => { return toks; };
|
case bytes.done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -2683,7 +2643,7 @@ export fn rsplitn(in: str, delim: str, n: i32) []str = {
|
|||||||
case bytes.done => void;
|
case bytes.done => void;
|
||||||
case let pk: str => {
|
case let pk: str => {
|
||||||
let r: str = remaining_tokens(&tok);
|
let r: str = remaining_tokens(&tok);
|
||||||
appendstr(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1244,28 +1244,6 @@ export fn remaining_tokens(s: *tokenizer) []u8 = {
|
|||||||
return s.in;
|
return s.in;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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 .len/.cap fields of a []u8 element (24B).
|
|
||||||
// Mirrors the same workaround in lib/shlex.shlex (appendstr, 16B) and
|
|
||||||
// lib/getopt.getopt (appendoption, 24B); collapses in one go when the
|
|
||||||
// append builtin learns to store the full element width.
|
|
||||||
@symbol("rt_ensure") fn rtensure(s: *void, membsz: u64) void;
|
|
||||||
|
|
||||||
// appendslice — grow `*slice` by one and store `item` (24B). Mirror
|
|
||||||
// of [[shlex.appendstr]] / [[getopt.appendoption]]. Bypasses the
|
|
||||||
// `append` builtin's first-8B-only-store gap for a slice-element.
|
|
||||||
fn appendslice(slice: *[][]u8, item: []u8) void = {
|
|
||||||
let newlen: i32 = slice.len + 1;
|
|
||||||
slice.len = newlen;
|
|
||||||
rtensure(slice: *void, 24u64);
|
|
||||||
let dst: *[]u8 = &slice.ptr[newlen - 1];
|
|
||||||
dst.ptr = item.ptr;
|
|
||||||
dst.len = item.len;
|
|
||||||
dst.cap = item.cap;
|
|
||||||
};
|
|
||||||
|
|
||||||
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
||||||
// tokens via forward iteration. The trailing slot (when more than
|
// tokens via forward iteration. The trailing slot (when more than
|
||||||
// `n - 1` tokens exist) holds the unconsumed remainder.
|
// `n - 1` tokens exist) holds the unconsumed remainder.
|
||||||
@@ -1290,7 +1268,7 @@ export fn splitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: []u8 => { appendslice(&toks, s); };
|
case let s: []u8 => { append(toks, s); };
|
||||||
case done => { return toks; };
|
case done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -1299,7 +1277,7 @@ export fn splitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
case done => void;
|
case done => void;
|
||||||
case let pk: []u8 => {
|
case let pk: []u8 => {
|
||||||
let r: []u8 = remaining_tokens(&tok);
|
let r: []u8 = remaining_tokens(&tok);
|
||||||
appendslice(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return toks;
|
return toks;
|
||||||
@@ -1328,7 +1306,7 @@ export fn rsplitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: []u8 => { appendslice(&toks, s); };
|
case let s: []u8 => { append(toks, s); };
|
||||||
case done => { return toks; };
|
case done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -1337,7 +1315,7 @@ export fn rsplitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
case done => void;
|
case done => void;
|
||||||
case let pk: []u8 => {
|
case let pk: []u8 => {
|
||||||
let r: []u8 = remaining_tokens(&tok);
|
let r: []u8 = remaining_tokens(&tok);
|
||||||
appendslice(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1955,7 +1933,7 @@ export fn dup(s: str) str = {
|
|||||||
// so the only nomem propagation point is the initial slice alloc.
|
// so the only nomem propagation point is the initial slice alloc.
|
||||||
// With no inner failure path, the rollback is structurally a no-op
|
// With no inner failure path, the rollback is structurally a no-op
|
||||||
// and is omitted; it returns once dup graduates to `(str | nomem)`
|
// and is omitted; it returns once dup graduates to `(str | nomem)`
|
||||||
// (#46). The pre-allocated slice has `cap == s.len`, so appendstr's
|
// (#46). The pre-allocated slice has `cap == s.len`, so append's
|
||||||
// rt_ensure call never reaches the grow branch.
|
// rt_ensure call never reaches the grow branch.
|
||||||
//
|
//
|
||||||
// Empty input bypasses the alloc: rt_malloc(0) is an mmap of 0 bytes
|
// Empty input bypasses the alloc: rt_malloc(0) is an mmap of 0 bytes
|
||||||
@@ -1974,7 +1952,7 @@ export fn dupall(s: []str) ([]str | nomem) = {
|
|||||||
let newsl: []str = alloc([], s.len)?;
|
let newsl: []str = alloc([], s.len)?;
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < s.len) {
|
for (i < s.len) {
|
||||||
appendstr(&newsl, dup(s[i]));
|
append(newsl, dup(s[i]));
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
return newsl;
|
return newsl;
|
||||||
@@ -2600,24 +2578,6 @@ export fn rcut(in: str, delim: str) (str, str) = {
|
|||||||
return (frombytes(a), frombytes(b));
|
return (frombytes(a), frombytes(b));
|
||||||
};
|
};
|
||||||
|
|
||||||
// rt_ensure is the runtime slice-growth helper invoked by the
|
|
||||||
// `append(s, v)` builtin. Direct bind for the same reason as
|
|
||||||
// lib/shlex.shlex (appendstr, 16B): the builtin's expansion stores
|
|
||||||
// only 8B of the new element, losing the `.len` half of a `str`.
|
|
||||||
@symbol("rt_ensure") fn rtensure(s: *void, membsz: u64) void;
|
|
||||||
|
|
||||||
// appendstr — grow `*slice` by one and store `item` (16B). Mirror of
|
|
||||||
// lib/shlex.shlex appendstr. Collapses when the append builtin learns
|
|
||||||
// to store the full element width.
|
|
||||||
fn appendstr(slice: *[]str, item: str) void = {
|
|
||||||
let newlen: i32 = slice.len + 1;
|
|
||||||
slice.len = newlen;
|
|
||||||
rtensure(slice: *void, size(str): u64);
|
|
||||||
let dst: *str = &slice.ptr[newlen - 1];
|
|
||||||
dst.ptr = item.ptr;
|
|
||||||
dst.len = item.len;
|
|
||||||
};
|
|
||||||
|
|
||||||
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
||||||
// tokens via forward iteration. The trailing slot (when more than
|
// tokens via forward iteration. The trailing slot (when more than
|
||||||
// `n - 1` tokens exist) holds the unconsumed remainder. Strings
|
// `n - 1` tokens exist) holds the unconsumed remainder. Strings
|
||||||
@@ -2640,7 +2600,7 @@ export fn splitn(in: str, delim: str, n: i32) []str = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: str => { appendstr(&toks, s); };
|
case let s: str => { append(toks, s); };
|
||||||
case bytes.done => { return toks; };
|
case bytes.done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -2649,7 +2609,7 @@ export fn splitn(in: str, delim: str, n: i32) []str = {
|
|||||||
case bytes.done => void;
|
case bytes.done => void;
|
||||||
case let pk: str => {
|
case let pk: str => {
|
||||||
let r: str = remaining_tokens(&tok);
|
let r: str = remaining_tokens(&tok);
|
||||||
appendstr(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return toks;
|
return toks;
|
||||||
@@ -2674,7 +2634,7 @@ export fn rsplitn(in: str, delim: str, n: i32) []str = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: str => { appendstr(&toks, s); };
|
case let s: str => { append(toks, s); };
|
||||||
case bytes.done => { return toks; };
|
case bytes.done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -2683,7 +2643,7 @@ export fn rsplitn(in: str, delim: str, n: i32) []str = {
|
|||||||
case bytes.done => void;
|
case bytes.done => void;
|
||||||
case let pk: str => {
|
case let pk: str => {
|
||||||
let r: str = remaining_tokens(&tok);
|
let r: str = remaining_tokens(&tok);
|
||||||
appendstr(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2738,28 +2738,6 @@ export fn remaining_tokens(s: *tokenizer) []u8 = {
|
|||||||
return s.in;
|
return s.in;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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 .len/.cap fields of a []u8 element (24B).
|
|
||||||
// Mirrors the same workaround in lib/shlex.shlex (appendstr, 16B) and
|
|
||||||
// lib/getopt.getopt (appendoption, 24B); collapses in one go when the
|
|
||||||
// append builtin learns to store the full element width.
|
|
||||||
@symbol("rt_ensure") fn rtensure(s: *void, membsz: u64) void;
|
|
||||||
|
|
||||||
// appendslice — grow `*slice` by one and store `item` (24B). Mirror
|
|
||||||
// of [[shlex.appendstr]] / [[getopt.appendoption]]. Bypasses the
|
|
||||||
// `append` builtin's first-8B-only-store gap for a slice-element.
|
|
||||||
fn appendslice(slice: *[][]u8, item: []u8) void = {
|
|
||||||
let newlen: i32 = slice.len + 1;
|
|
||||||
slice.len = newlen;
|
|
||||||
rtensure(slice: *void, 24u64);
|
|
||||||
let dst: *[]u8 = &slice.ptr[newlen - 1];
|
|
||||||
dst.ptr = item.ptr;
|
|
||||||
dst.len = item.len;
|
|
||||||
dst.cap = item.cap;
|
|
||||||
};
|
|
||||||
|
|
||||||
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
||||||
// tokens via forward iteration. The trailing slot (when more than
|
// tokens via forward iteration. The trailing slot (when more than
|
||||||
// `n - 1` tokens exist) holds the unconsumed remainder.
|
// `n - 1` tokens exist) holds the unconsumed remainder.
|
||||||
@@ -2784,7 +2762,7 @@ export fn splitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: []u8 => { appendslice(&toks, s); };
|
case let s: []u8 => { append(toks, s); };
|
||||||
case done => { return toks; };
|
case done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -2793,7 +2771,7 @@ export fn splitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
case done => void;
|
case done => void;
|
||||||
case let pk: []u8 => {
|
case let pk: []u8 => {
|
||||||
let r: []u8 = remaining_tokens(&tok);
|
let r: []u8 = remaining_tokens(&tok);
|
||||||
appendslice(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return toks;
|
return toks;
|
||||||
@@ -2822,7 +2800,7 @@ export fn rsplitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: []u8 => { appendslice(&toks, s); };
|
case let s: []u8 => { append(toks, s); };
|
||||||
case done => { return toks; };
|
case done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -2831,7 +2809,7 @@ export fn rsplitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|||||||
case done => void;
|
case done => void;
|
||||||
case let pk: []u8 => {
|
case let pk: []u8 => {
|
||||||
let r: []u8 = remaining_tokens(&tok);
|
let r: []u8 = remaining_tokens(&tok);
|
||||||
appendslice(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -3472,7 +3450,7 @@ export fn dup(s: str) str = {
|
|||||||
// so the only nomem propagation point is the initial slice alloc.
|
// so the only nomem propagation point is the initial slice alloc.
|
||||||
// With no inner failure path, the rollback is structurally a no-op
|
// With no inner failure path, the rollback is structurally a no-op
|
||||||
// and is omitted; it returns once dup graduates to `(str | nomem)`
|
// and is omitted; it returns once dup graduates to `(str | nomem)`
|
||||||
// (#46). The pre-allocated slice has `cap == s.len`, so appendstr's
|
// (#46). The pre-allocated slice has `cap == s.len`, so append's
|
||||||
// rt_ensure call never reaches the grow branch.
|
// rt_ensure call never reaches the grow branch.
|
||||||
//
|
//
|
||||||
// Empty input bypasses the alloc: rt_malloc(0) is an mmap of 0 bytes
|
// Empty input bypasses the alloc: rt_malloc(0) is an mmap of 0 bytes
|
||||||
@@ -3491,7 +3469,7 @@ export fn dupall(s: []str) ([]str | nomem) = {
|
|||||||
let newsl: []str = alloc([], s.len)?;
|
let newsl: []str = alloc([], s.len)?;
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < s.len) {
|
for (i < s.len) {
|
||||||
appendstr(&newsl, dup(s[i]));
|
append(newsl, dup(s[i]));
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
return newsl;
|
return newsl;
|
||||||
@@ -4117,24 +4095,6 @@ export fn rcut(in: str, delim: str) (str, str) = {
|
|||||||
return (frombytes(a), frombytes(b));
|
return (frombytes(a), frombytes(b));
|
||||||
};
|
};
|
||||||
|
|
||||||
// rt_ensure is the runtime slice-growth helper invoked by the
|
|
||||||
// `append(s, v)` builtin. Direct bind for the same reason as
|
|
||||||
// lib/shlex.shlex (appendstr, 16B): the builtin's expansion stores
|
|
||||||
// only 8B of the new element, losing the `.len` half of a `str`.
|
|
||||||
@symbol("rt_ensure") fn rtensure(s: *void, membsz: u64) void;
|
|
||||||
|
|
||||||
// appendstr — grow `*slice` by one and store `item` (16B). Mirror of
|
|
||||||
// lib/shlex.shlex appendstr. Collapses when the append builtin learns
|
|
||||||
// to store the full element width.
|
|
||||||
fn appendstr(slice: *[]str, item: str) void = {
|
|
||||||
let newlen: i32 = slice.len + 1;
|
|
||||||
slice.len = newlen;
|
|
||||||
rtensure(slice: *void, size(str): u64);
|
|
||||||
let dst: *str = &slice.ptr[newlen - 1];
|
|
||||||
dst.ptr = item.ptr;
|
|
||||||
dst.len = item.len;
|
|
||||||
};
|
|
||||||
|
|
||||||
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
||||||
// tokens via forward iteration. The trailing slot (when more than
|
// tokens via forward iteration. The trailing slot (when more than
|
||||||
// `n - 1` tokens exist) holds the unconsumed remainder. Strings
|
// `n - 1` tokens exist) holds the unconsumed remainder. Strings
|
||||||
@@ -4157,7 +4117,7 @@ export fn splitn(in: str, delim: str, n: i32) []str = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: str => { appendstr(&toks, s); };
|
case let s: str => { append(toks, s); };
|
||||||
case bytes.done => { return toks; };
|
case bytes.done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -4166,7 +4126,7 @@ export fn splitn(in: str, delim: str, n: i32) []str = {
|
|||||||
case bytes.done => void;
|
case bytes.done => void;
|
||||||
case let pk: str => {
|
case let pk: str => {
|
||||||
let r: str = remaining_tokens(&tok);
|
let r: str = remaining_tokens(&tok);
|
||||||
appendstr(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
return toks;
|
return toks;
|
||||||
@@ -4191,7 +4151,7 @@ export fn rsplitn(in: str, delim: str, n: i32) []str = {
|
|||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < n - 1) {
|
for (i < n - 1) {
|
||||||
match (next_token(&tok)) {
|
match (next_token(&tok)) {
|
||||||
case let s: str => { appendstr(&toks, s); };
|
case let s: str => { append(toks, s); };
|
||||||
case bytes.done => { return toks; };
|
case bytes.done => { return toks; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -4200,7 +4160,7 @@ export fn rsplitn(in: str, delim: str, n: i32) []str = {
|
|||||||
case bytes.done => void;
|
case bytes.done => void;
|
||||||
case let pk: str => {
|
case let pk: str => {
|
||||||
let r: str = remaining_tokens(&tok);
|
let r: str = remaining_tokens(&tok);
|
||||||
appendstr(&toks, r);
|
append(toks, r);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user