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

@@ -747,6 +747,134 @@ fn expect_tok(toks: [][]u8, i: i32, want: []u8) void = {
os.free(t2.ptr: *void, (t2.cap: u64) * 24u64);
};
// ---- cut / rcut -------------------------------------------------------
// ref/hare/bytes/tokenize.ha:429 (@test fn cut). Borrowed views; each
// half asserted via equal(). Needle order is ww's (u8 | []u8). Rows use
// the destructure form `let (before, after) = cut(...)` to drive the #10
// over-cap tuple-return (sret) end-to-end.
@test fn cut_cases() void = {
let z: [1]u8;
let abc: [3]u8; abc[0] = 97u8; abc[1] = 98u8; abc[2] = 99u8;
let ea: [1]u8; ea[0] = 97u8;
let ec: [1]u8; ec[0] = 99u8;
// ['a','b','c'] along byte 'b' -> ['a'],['c']
signalled = 1810;
let (b0, a0) = bytes.cut(abc[0:3], 98u8);
if (!bytes.equal(b0, ea[0:1])) { fail(); };
if (!bytes.equal(a0, ec[0:1])) { fail(); };
// same, single-byte []u8 delim -> identical
signalled = 1811;
let nb: [1]u8; nb[0] = 98u8;
let (b1, a1) = bytes.cut(abc[0:3], nb[0:1]);
if (!bytes.equal(b1, ea[0:1])) { fail(); };
if (!bytes.equal(a1, ec[0:1])) { fail(); };
// ['b','c'] along 'b' -> [],['c'] (empty before)
signalled = 1812;
let bc: [2]u8; bc[0] = 98u8; bc[1] = 99u8;
let (b2, a2) = bytes.cut(bc[0:2], 98u8);
if (!bytes.equal(b2, z[0:0])) { fail(); };
if (!bytes.equal(a2, ec[0:1])) { fail(); };
// ['a','b'] along 'b' -> ['a'],[] (empty after)
signalled = 1813;
let ab: [2]u8; ab[0] = 97u8; ab[1] = 98u8;
let (b3, a3) = bytes.cut(ab[0:2], 98u8);
if (!bytes.equal(b3, ea[0:1])) { fail(); };
if (!bytes.equal(a3, z[0:0])) { fail(); };
// delim absent -> (whole input, [])
signalled = 1814;
let (b4, a4) = bytes.cut(abc[0:3], 120u8);
if (!bytes.equal(b4, abc[0:3])) { fail(); };
if (!bytes.equal(a4, z[0:0])) { fail(); };
// empty input -> ([],[])
signalled = 1815;
let (b5, a5) = bytes.cut(z[0:0], 120u8);
if (!bytes.equal(b5, z[0:0])) { fail(); };
if (!bytes.equal(a5, z[0:0])) { fail(); };
// repeated delim -> FIRST instance: ['a'],['c','b','a']
signalled = 1816;
let abcba: [5]u8;
abcba[0] = 97u8; abcba[1] = 98u8; abcba[2] = 99u8;
abcba[3] = 98u8; abcba[4] = 97u8;
let ecba: [3]u8; ecba[0] = 99u8; ecba[1] = 98u8; ecba[2] = 97u8;
let (b6, a6) = bytes.cut(abcba[0:5], 98u8);
if (!bytes.equal(b6, ea[0:1])) { fail(); };
if (!bytes.equal(a6, ecba[0:3])) { fail(); };
// 2-byte []u8 delim present — "ab<XY>c" -> ['a','b'],['c']
signalled = 1817;
let h: [5]u8;
h[0] = 97u8; h[1] = 98u8; h[2] = 88u8; h[3] = 89u8; h[4] = 99u8;
let xy: [2]u8; xy[0] = 88u8; xy[1] = 89u8;
let eab: [2]u8; eab[0] = 97u8; eab[1] = 98u8;
let (b7, a7) = bytes.cut(h[0:5], xy[0:2]);
if (!bytes.equal(b7, eab[0:2])) { fail(); };
if (!bytes.equal(a7, ec[0:1])) { fail(); };
// 2-byte []u8 delim absent -> (whole, [])
signalled = 1818;
let zz: [2]u8; zz[0] = 9u8; zz[1] = 9u8;
let (b8, a8) = bytes.cut(h[0:5], zz[0:2]);
if (!bytes.equal(b8, h[0:5])) { fail(); };
if (!bytes.equal(a8, z[0:0])) { fail(); };
};
@test fn rcut_cases() void = {
let z: [1]u8;
let ea: [1]u8; ea[0] = 97u8;
// ['a','b','c'] along 'b' -> ['a'],['c'] (single instance == cut)
signalled = 1820;
let abc: [3]u8; abc[0] = 97u8; abc[1] = 98u8; abc[2] = 99u8;
let ec: [1]u8; ec[0] = 99u8;
let (b0, a0) = bytes.rcut(abc[0:3], 98u8);
if (!bytes.equal(b0, ea[0:1])) { fail(); };
if (!bytes.equal(a0, ec[0:1])) { fail(); };
// repeated delim -> LAST instance: ['a','b','c'],['a']
signalled = 1821;
let abcba: [5]u8;
abcba[0] = 97u8; abcba[1] = 98u8; abcba[2] = 99u8;
abcba[3] = 98u8; abcba[4] = 97u8;
let eabc: [3]u8; eabc[0] = 97u8; eabc[1] = 98u8; eabc[2] = 99u8;
let (b1, a1) = bytes.rcut(abcba[0:5], 98u8);
if (!bytes.equal(b1, eabc[0:3])) { fail(); };
if (!bytes.equal(a1, ea[0:1])) { fail(); };
// delim absent -> (whole input, [])
signalled = 1822;
let (b2, a2) = bytes.rcut(abc[0:3], 120u8);
if (!bytes.equal(b2, abc[0:3])) { fail(); };
if (!bytes.equal(a2, z[0:0])) { fail(); };
// 2-byte []u8 delim present, two instances -> cut at LAST.
// "<XY>a<XY>b" -> ['X','Y','a'],['b']
signalled = 1823;
let h: [6]u8;
h[0] = 88u8; h[1] = 89u8; h[2] = 97u8;
h[3] = 88u8; h[4] = 89u8; h[5] = 98u8;
let xy: [2]u8; xy[0] = 88u8; xy[1] = 89u8;
let exya: [3]u8; exya[0] = 88u8; exya[1] = 89u8; exya[2] = 97u8;
let eb: [1]u8; eb[0] = 98u8;
let (b3, a3) = bytes.rcut(h[0:6], xy[0:2]);
if (!bytes.equal(b3, exya[0:3])) { fail(); };
if (!bytes.equal(a3, eb[0:1])) { fail(); };
// 2-byte []u8 delim absent -> (whole, [])
signalled = 1824;
let zz: [2]u8; zz[0] = 9u8; zz[1] = 9u8;
let (b4, a4) = bytes.rcut(h[0:6], zz[0:2]);
if (!bytes.equal(b4, h[0:6])) { fail(); };
if (!bytes.equal(a4, z[0:0])) { fail(); };
};
export fn main() i32 = {
signalled = 1; equal_cases();
signalled = 2; index_byte_cases();
@@ -766,5 +894,7 @@ export fn main() i32 = {
signalled = 16; ltrim_cases();
signalled = 17; rtrim_cases();
signalled = 18; trim_cases();
signalled = 19; cut_cases();
signalled = 20; rcut_cases();
return 0;
};