lib/bytes: add cut and rcut (#4)

Port bytes::cut / bytes::rcut from ref/hare/bytes/tokenize.ha:392,413.
Both return borrowed (before, after) views split on the first / last
delimiter instance; void-case yields (whole input, empty). Needle order
is ww's (u8 | []u8), matching index/rindex (bytes.ww:57/91) rather than
Hare's ([]u8 | u8).

Unblocked by #10 (wide tuple-return / sret): ([]u8, []u8) is 48B,
over-cap, returned via sret and received by the call-site destructure
the tests exercise. combined.ww amalgamations regenerated (bytes is
compiler-imported via strings).
This commit is contained in:
2026-06-01 15:02:12 +09:00
parent d0a1cb1ca3
commit cdb74e8a49
8 changed files with 508 additions and 0 deletions

View File

@@ -516,3 +516,57 @@ export fn rsplitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
export fn split(in: []u8, delim: []u8) [][]u8 = {
return splitn(in, delim, types.I32_MAX);
};
// cut — split `in` along the first instance of `delim`, returning the
// portion before and the portion after the delimiter as a borrowed
// tuple. When `delim` is absent, the whole input is the first half and
// the second is empty. ref/hare/bytes/tokenize.ha:392.
//
// Delim is spelled (u8 | []u8) to match index/rindex (bytes.ww:57/91);
// the tagged union is an unordered set, so this is the same type as
// Hare's ([]u8 | u8), not a divergence.
export fn cut(in: []u8, delim: (u8 | []u8)) ([]u8, []u8) = {
let ln: i32 = match (delim) {
case let c: u8 => yield 1i32;
case let sub: []u8 => {
os.assert(sub.len > 0,
"bytes.cut called with empty delimiter");
yield sub.len;
};
};
match (index(in, delim)) {
case let i: i32 => {
let lo: i32 = i + ln;
return (in[0:i], in[lo:in.len]);
};
case void => {
let empty: []u8;
empty.ptr = nil; empty.len = 0; empty.cap = 0;
return (in, empty);
};
};
};
// rcut — like [[cut]] but splits along the last instance of `delim`.
// ref/hare/bytes/tokenize.ha:413.
export fn rcut(in: []u8, delim: (u8 | []u8)) ([]u8, []u8) = {
let ln: i32 = match (delim) {
case let c: u8 => yield 1i32;
case let sub: []u8 => {
os.assert(sub.len > 0,
"bytes.rcut called with empty delimiter");
yield sub.len;
};
};
match (rindex(in, delim)) {
case let i: i32 => {
let lo: i32 = i + ln;
return (in[0:i], in[lo:in.len]);
};
case void => {
let empty: []u8;
empty.ptr = nil; empty.len = 0; empty.cap = 0;
return (in, empty);
};
};
};