// bytestest — exercises lib/bytes. Run with // `out/bin/ww run lib/bytes/bytestest.ww`. Same signalled-then- // fail()-with-+10 pattern as hex / utf8 / time tests: non-zero exit // pinpoints the failing scenario. // // Vectors mirror Hare's @test fns in ref/hare/bytes/equal.ha, // ref/hare/bytes/index.ha, ref/hare/bytes/contains.ha. package bytes; import bytes; import os; let signalled: i32 = 0; fn fail() void = { os.exit(signalled + 10); }; // ---- equal ------------------------------------------------------------ // ref/hare/bytes/equal.ha:21. @test fn equal_cases() void = { let a: [3]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 3u8; let b: [3]u8; b[0] = 1u8; b[1] = 2u8; b[2] = 3u8; let c: [3]u8; c[0] = 1u8; c[1] = 4u8; c[2] = 5u8; let d: [4]u8; d[0] = 1u8; d[1] = 2u8; d[2] = 3u8; d[3] = 4u8; let e: [2]u8; e[0] = 1u8; e[1] = 2u8; let z: [1]u8; if (!bytes.equal(a[0:3], b[0:3])) { fail(); }; if ( bytes.equal(a[0:3], c[0:3])) { fail(); }; if ( bytes.equal(a[0:3], d[0:4])) { fail(); }; if ( bytes.equal(a[0:3], e[0:2])) { fail(); }; if (!bytes.equal(z[0:0], z[0:0])) { fail(); }; // empty-empty }; // ---- index(u8) -------------------------------------------------------- // ref/hare/bytes/index.ha:112. @test fn index_byte_cases() void = { let a: [4]u8; a[0] = 1u8; a[1] = 3u8; a[2] = 3u8; a[3] = 7u8; match (bytes.index(a[0:4], 1u8)) { case let i: i32 => { if (i != 0) { fail(); }; }; case void => { fail(); }; }; match (bytes.index(a[0:4], 3u8)) { case let i: i32 => { if (i != 1) { fail(); }; }; case void => { fail(); }; }; match (bytes.index(a[0:4], 7u8)) { case let i: i32 => { if (i != 3) { fail(); }; }; case void => { fail(); }; }; match (bytes.index(a[0:4], 42u8)) { case let i: i32 => { fail(); }; case void => void; }; let z: [1]u8; match (bytes.index(z[0:0], 42u8)) { case let i: i32 => { fail(); }; case void => void; }; }; // ---- index([]u8) ------------------------------------------------------ // ref/hare/bytes/index.ha:128-139. Vector strings copied verbatim where // representable as ASCII byte sequences. @test fn index_slice_cases() void = { let h1: [4]u8; h1[0] = 1u8; h1[1] = 42u8; h1[2] = 24u8; h1[3] = 0u8; let n1: [2]u8; n1[0] = 42u8; n1[1] = 24u8; match (bytes.index(h1[0:3], n1[0:2])) { case let i: i32 => { if (i != 1) { fail(); }; }; case void => { fail(); }; }; let h2: [4]u8; h2[0] = 1u8; h2[1] = 3u8; h2[2] = 3u8; h2[3] = 7u8; let n2: [2]u8; n2[0] = 3u8; n2[1] = 3u8; match (bytes.index(h2[0:4], n2[0:2])) { case let i: i32 => { if (i != 1) { fail(); }; }; case void => { fail(); }; }; // needle longer than haystack — void let h3: [3]u8; h3[0] = 1u8; h3[1] = 2u8; h3[2] = 3u8; let n3: [4]u8; n3[0] = 1u8; n3[1] = 2u8; n3[2] = 3u8; n3[3] = 4u8; match (bytes.index(h3[0:3], n3[0:4])) { case let i: i32 => { fail(); }; case void => void; }; // len(haystack) == len(needle), match — offset 0 let h4: [2]u8; h4[0] = 42u8; h4[1] = 20u8; let n4: [2]u8; n4[0] = 42u8; n4[1] = 20u8; match (bytes.index(h4[0:2], n4[0:2])) { case let i: i32 => { if (i != 0) { fail(); }; }; case void => { fail(); }; }; // len(haystack) == len(needle), no match — void let h5: [4]u8; h5[0] = 1u8; h5[1] = 1u8; h5[2] = 1u8; h5[3] = 2u8; let n5: [4]u8; n5[0] = 1u8; n5[1] = 1u8; n5[2] = 1u8; n5[3] = 3u8; match (bytes.index(h5[0:4], n5[0:4])) { case let i: i32 => { fail(); }; case void => void; }; // Partial-prefix recovery — needle [1,1,2] aligns at i=1 after the // false-start at i=0 ([1,1,1] mismatches at byte 2). Pins the // naive scanner's restart discipline. let h6: [4]u8; h6[0] = 1u8; h6[1] = 1u8; h6[2] = 1u8; h6[3] = 2u8; let n6s: [3]u8; n6s[0] = 1u8; n6s[1] = 1u8; n6s[2] = 2u8; match (bytes.index(h6[0:4], n6s[0:3])) { case let i: i32 => { if (i != 1) { fail(); }; }; case void => { fail(); }; }; // Same shape, longer haystack with no match anywhere. let h7: [5]u8; h7[0] = 1u8; h7[1] = 1u8; h7[2] = 1u8; h7[3] = 3u8; h7[4] = 2u8; let n7: [4]u8; n7[0] = 1u8; n7[1] = 1u8; n7[2] = 1u8; n7[3] = 2u8; match (bytes.index(h7[0:5], n7[0:4])) { case let i: i32 => { fail(); }; case void => void; }; // empty needle — Hare returns 0 (ref/hare/bytes/index.ha:63). let z: [1]u8; let zn: [1]u8; match (bytes.index(h2[0:4], zn[0:0])) { case let i: i32 => { if (i != 0) { fail(); }; }; case void => { fail(); }; }; // empty haystack, non-empty needle — void match (bytes.index(z[0:0], n3[0:3])) { case let i: i32 => { fail(); }; case void => void; }; // single-byte slice needle — should semantically equal u8 arm let n6: [1]u8; n6[0] = 7u8; match (bytes.index(h2[0:4], n6[0:1])) { case let i: i32 => { if (i != 3) { fail(); }; }; case void => { fail(); }; }; }; // ---- rindex(u8) ------------------------------------------------------- // ref/hare/bytes/index.ha:118. @test fn rindex_byte_cases() void = { let a: [4]u8; a[0] = 1u8; a[1] = 3u8; a[2] = 3u8; a[3] = 7u8; match (bytes.rindex(a[0:4], 3u8)) { case let i: i32 => { if (i != 2) { fail(); }; }; case void => { fail(); }; }; match (bytes.rindex(a[0:4], 42u8)) { case let i: i32 => { fail(); }; case void => void; }; let z: [1]u8; match (bytes.rindex(z[0:0], 42u8)) { case let i: i32 => { fail(); }; case void => void; }; }; // ---- rindex([]u8) ----------------------------------------------------- // ref/hare/bytes/index.ha:123-125. Distinguishes from index when the // needle appears more than once. @test fn rindex_slice_cases() void = { let a: [4]u8; a[0] = 1u8; a[1] = 1u8; a[2] = 1u8; a[3] = 2u8; let n11: [2]u8; n11[0] = 1u8; n11[1] = 1u8; match (bytes.rindex(a[0:4], n11[0:2])) { case let i: i32 => { if (i != 1) { fail(); }; }; case void => { fail(); }; }; let n12: [2]u8; n12[0] = 1u8; n12[1] = 2u8; match (bytes.rindex(a[0:4], n12[0:2])) { case let i: i32 => { if (i != 2) { fail(); }; }; case void => { fail(); }; }; // absent let n99: [2]u8; n99[0] = 9u8; n99[1] = 9u8; match (bytes.rindex(a[0:4], n99[0:2])) { case let i: i32 => { fail(); }; case void => void; }; }; // ---- contains --------------------------------------------------------- @test fn contains_cases() void = { let a: [4]u8; a[0] = 1u8; a[1] = 3u8; a[2] = 3u8; a[3] = 7u8; if (!bytes.contains(a[0:4], 7u8)) { fail(); }; if ( bytes.contains(a[0:4], 42u8)) { fail(); }; let n: [2]u8; n[0] = 3u8; n[1] = 3u8; if (!bytes.contains(a[0:4], n[0:2])) { fail(); }; let m: [2]u8; m[0] = 9u8; m[1] = 9u8; if ( bytes.contains(a[0:4], m[0:2])) { fail(); }; // Variadic rows. ref/hare/bytes/contains.ha:6. signalled = 1700; if ( bytes.contains(a[0:4])) { fail(); }; signalled = 1701; if (!bytes.contains(a[0:4], n[0:2])) { fail(); }; signalled = 1702; if (!bytes.contains(a[0:4], 7u8)) { fail(); }; signalled = 1703; if (!bytes.contains(a[0:4], m[0:2], n[0:2], 42u8)) { fail(); }; signalled = 1704; if ( bytes.contains(a[0:4], m[0:2], 42u8, m[0:2])) { fail(); }; }; // ---- hasprefix -------------------------------------------------------- // ref/hare/bytes/contains.ha:25. @test fn hasprefix_cases() void = { let z: [1]u8; if (!bytes.hasprefix(z[0:0], z[0:0])) { fail(); }; let one: [1]u8; one[0] = 0u8; if (!bytes.hasprefix(one[0:1], z[0:0])) { fail(); }; if ( bytes.hasprefix(z[0:0], one[0:1])) { fail(); }; let a: [3]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 3u8; let p12: [2]u8; p12[0] = 1u8; p12[1] = 2u8; if (!bytes.hasprefix(a[0:3], p12[0:2])) { fail(); }; let p11: [2]u8; p11[0] = 1u8; p11[1] = 1u8; if ( bytes.hasprefix(a[0:3], p11[0:2])) { fail(); }; let pl: [4]u8; pl[0] = 1u8; pl[1] = 2u8; pl[2] = 3u8; pl[3] = 4u8; if ( bytes.hasprefix(a[0:3], pl[0:4])) { fail(); }; }; // ---- hassuffix -------------------------------------------------------- // ref/hare/bytes/contains.ha:40. @test fn hassuffix_cases() void = { let z: [1]u8; if (!bytes.hassuffix(z[0:0], z[0:0])) { fail(); }; let one: [1]u8; one[0] = 0u8; if (!bytes.hassuffix(one[0:1], z[0:0])) { fail(); }; if ( bytes.hassuffix(z[0:0], one[0:1])) { fail(); }; let a: [3]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 3u8; let s23: [2]u8; s23[0] = 2u8; s23[1] = 3u8; if (!bytes.hassuffix(a[0:3], s23[0:2])) { fail(); }; let s22: [2]u8; s22[0] = 2u8; s22[1] = 2u8; if ( bytes.hassuffix(a[0:3], s22[0:2])) { fail(); }; let a4: [4]u8; a4[0] = 1u8; a4[1] = 2u8; a4[2] = 3u8; a4[3] = 4u8; let s234: [3]u8; s234[0] = 2u8; s234[1] = 3u8; s234[2] = 4u8; if (!bytes.hassuffix(a4[0:4], s234[0:3])) { fail(); }; }; // ---- tokenize / rtokenize / peek_token / remaining_tokens ----------- // ref/hare/bytes/tokenize.ha:258. Hare's @test fn tokenize / rtokenize // drives the iterator through an expected-token sequence and asserts // `equal(p, n)` (peek == next), `equal(n, want)` (next == expected). // expect_token — table row driver. Advances `t` once, asserts the // returned token matches `want`. peek invariant: peek must equal next. fn expect_token(t: *bytes.tokenizer, want: []u8) void = { match (bytes.peek_token(t)) { case let p: []u8 => { if (!bytes.equal(p, want)) { fail(); }; }; case bytes.done => { fail(); }; }; match (bytes.next_token(t)) { case let n: []u8 => { if (!bytes.equal(n, want)) { fail(); }; }; case bytes.done => { fail(); }; }; }; // expect_done — table-row driver. peek and next must both be done. fn expect_done(t: *bytes.tokenizer) void = { match (bytes.peek_token(t)) { case let p: []u8 => { fail(); }; case bytes.done => void; }; match (bytes.next_token(t)) { case let n: []u8 => { fail(); }; case bytes.done => void; }; }; @test fn tokenize_cases() void = { let z: [1]u8; // simple — [1,2,0,3,4] / [0] -> [1,2],[3,4] signalled = 1710; let a: [5]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 0u8; a[3] = 3u8; a[4] = 4u8; let e_12: [2]u8; e_12[0] = 1u8; e_12[1] = 2u8; let e_34: [2]u8; e_34[0] = 3u8; e_34[1] = 4u8; let t: bytes.tokenizer = bytes.tokenize(a[0:5], 0u8); expect_token(&t, e_12[0:2]); expect_token(&t, e_34[0:2]); expect_done(&t); // multiple delimiters — [1,2,0,3,4,42,5,6] / [0,42] -> [1,2],[3,4],[5,6] signalled = 1711; let b: [8]u8; b[0] = 1u8; b[1] = 2u8; b[2] = 0u8; b[3] = 3u8; b[4] = 4u8; b[5] = 42u8; b[6] = 5u8; b[7] = 6u8; let e_56: [2]u8; e_56[0] = 5u8; e_56[1] = 6u8; let t2: bytes.tokenizer = bytes.tokenize(b[0:8], 0u8, 42u8); expect_token(&t2, e_12[0:2]); expect_token(&t2, e_34[0:2]); expect_token(&t2, e_56[0:2]); expect_done(&t2); // empty interior tokens — [1,2,0,0,0,3,4] / [0] -> [1,2],[],[],[3,4] signalled = 1712; let c: [7]u8; c[0] = 1u8; c[1] = 2u8; c[2] = 0u8; c[3] = 0u8; c[4] = 0u8; c[5] = 3u8; c[6] = 4u8; let t3: bytes.tokenizer = bytes.tokenize(c[0:7], 0u8); expect_token(&t3, e_12[0:2]); expect_token(&t3, z[0:0]); expect_token(&t3, z[0:0]); expect_token(&t3, e_34[0:2]); expect_done(&t3); // leading + trailing empty — [0,1,2,3,0] / [0] -> [],[1,2,3],[] signalled = 1713; let d: [5]u8; d[0] = 0u8; d[1] = 1u8; d[2] = 2u8; d[3] = 3u8; d[4] = 0u8; let e_123: [3]u8; e_123[0] = 1u8; e_123[1] = 2u8; e_123[2] = 3u8; let t4: bytes.tokenizer = bytes.tokenize(d[0:5], 0u8); expect_token(&t4, z[0:0]); expect_token(&t4, e_123[0:3]); expect_token(&t4, z[0:0]); expect_done(&t4); // no delim hit — [1,2,3] / [0] -> [1,2,3] signalled = 1714; let f: [3]u8; f[0] = 1u8; f[1] = 2u8; f[2] = 3u8; let t5: bytes.tokenizer = bytes.tokenize(f[0:3], 0u8); expect_token(&t5, e_123[0:3]); expect_done(&t5); // empty input — [] / [0] -> done immediately signalled = 1715; let t6: bytes.tokenizer = bytes.tokenize(z[0:0], 0u8); expect_done(&t6); }; @test fn rtokenize_cases() void = { let z: [1]u8; // simple — [1,2,0,3,4] / [0] -> [3,4],[1,2] signalled = 1720; let a: [5]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 0u8; a[3] = 3u8; a[4] = 4u8; let e_12: [2]u8; e_12[0] = 1u8; e_12[1] = 2u8; let e_34: [2]u8; e_34[0] = 3u8; e_34[1] = 4u8; let t: bytes.tokenizer = bytes.rtokenize(a[0:5], 0u8); expect_token(&t, e_34[0:2]); expect_token(&t, e_12[0:2]); expect_done(&t); // multiple delimiters — [1,2,0,3,4,42,5,6] / [0,42] -> [5,6],[3,4],[1,2] signalled = 1721; let b: [8]u8; b[0] = 1u8; b[1] = 2u8; b[2] = 0u8; b[3] = 3u8; b[4] = 4u8; b[5] = 42u8; b[6] = 5u8; b[7] = 6u8; let e_56: [2]u8; e_56[0] = 5u8; e_56[1] = 6u8; let t2: bytes.tokenizer = bytes.rtokenize(b[0:8], 0u8, 42u8); expect_token(&t2, e_56[0:2]); expect_token(&t2, e_34[0:2]); expect_token(&t2, e_12[0:2]); expect_done(&t2); // empty interior tokens — [1,2,0,0,0,3,4] / [0] -> [3,4],[],[],[1,2] signalled = 1722; let c: [7]u8; c[0] = 1u8; c[1] = 2u8; c[2] = 0u8; c[3] = 0u8; c[4] = 0u8; c[5] = 3u8; c[6] = 4u8; let t3: bytes.tokenizer = bytes.rtokenize(c[0:7], 0u8); expect_token(&t3, e_34[0:2]); expect_token(&t3, z[0:0]); expect_token(&t3, z[0:0]); expect_token(&t3, e_12[0:2]); expect_done(&t3); // leading + trailing empty — [0,1,2,3,0] / [0] -> [],[1,2,3],[] signalled = 1723; let d: [5]u8; d[0] = 0u8; d[1] = 1u8; d[2] = 2u8; d[3] = 3u8; d[4] = 0u8; let e_123: [3]u8; e_123[0] = 1u8; e_123[1] = 2u8; e_123[2] = 3u8; let t4: bytes.tokenizer = bytes.rtokenize(d[0:5], 0u8); expect_token(&t4, z[0:0]); expect_token(&t4, e_123[0:3]); expect_token(&t4, z[0:0]); expect_done(&t4); // no delim hit — [1,2,3] / [0] -> [1,2,3] signalled = 1724; let f: [3]u8; f[0] = 1u8; f[1] = 2u8; f[2] = 3u8; let t5: bytes.tokenizer = bytes.rtokenize(f[0:3], 0u8); expect_token(&t5, e_123[0:3]); expect_done(&t5); // empty input — [] / [0] -> done immediately signalled = 1725; let t6: bytes.tokenizer = bytes.rtokenize(z[0:0], 0u8); expect_done(&t6); }; @test fn peek_token_cases() void = { // Peeking twice without advancing returns the same token. signalled = 1730; let a: [5]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 0u8; a[3] = 3u8; a[4] = 4u8; let e_12: [2]u8; e_12[0] = 1u8; e_12[1] = 2u8; let t: bytes.tokenizer = bytes.tokenize(a[0:5], 0u8); match (bytes.peek_token(&t)) { case let p: []u8 => { if (!bytes.equal(p, e_12[0:2])) { fail(); }; }; case bytes.done => { fail(); }; }; match (bytes.peek_token(&t)) { case let p: []u8 => { if (!bytes.equal(p, e_12[0:2])) { fail(); }; }; case bytes.done => { fail(); }; }; // Then advance once — peek-after-next is the second token. signalled = 1731; let e_34: [2]u8; e_34[0] = 3u8; e_34[1] = 4u8; match (bytes.next_token(&t)) { case let n: []u8 => { if (!bytes.equal(n, e_12[0:2])) { fail(); }; }; case bytes.done => { fail(); }; }; match (bytes.peek_token(&t)) { case let p: []u8 => { if (!bytes.equal(p, e_34[0:2])) { fail(); }; }; case bytes.done => { fail(); }; }; // Reverse peek symmetry — first peek is last token. signalled = 1732; let t2: bytes.tokenizer = bytes.rtokenize(a[0:5], 0u8); match (bytes.peek_token(&t2)) { case let p: []u8 => { if (!bytes.equal(p, e_34[0:2])) { fail(); }; }; case bytes.done => { fail(); }; }; match (bytes.peek_token(&t2)) { case let p: []u8 => { if (!bytes.equal(p, e_34[0:2])) { fail(); }; }; case bytes.done => { fail(); }; }; }; @test fn remaining_tokens_cases() void = { // After one next_token, remaining is bytes past the consumed delim. signalled = 1740; let a: [5]u8; a[0] = 1u8; a[1] = 2u8; a[2] = 0u8; a[3] = 3u8; a[4] = 4u8; let e_34: [2]u8; e_34[0] = 3u8; e_34[1] = 4u8; let t: bytes.tokenizer = bytes.tokenize(a[0:5], 0u8); match (bytes.next_token(&t)) { case let n: []u8 => void; case bytes.done => { fail(); }; }; let r: []u8 = bytes.remaining_tokens(&t); if (!bytes.equal(r, e_34[0:2])) { fail(); }; // Reverse — after one next_token, remaining is bytes before the // consumed delim. ref/hare/bytes/tokenize.ha:323-327 pins [1,2]. signalled = 1741; let e_12: [2]u8; e_12[0] = 1u8; e_12[1] = 2u8; let t2: bytes.tokenizer = bytes.rtokenize(a[0:5], 0u8); match (bytes.next_token(&t2)) { case let n: []u8 => void; case bytes.done => { fail(); }; }; let r2: []u8 = bytes.remaining_tokens(&t2); if (!bytes.equal(r2, e_12[0:2])) { fail(); }; }; export fn main() i32 = { signalled = 1; equal_cases(); signalled = 2; index_byte_cases(); signalled = 3; index_slice_cases(); signalled = 4; rindex_byte_cases(); signalled = 5; rindex_slice_cases(); signalled = 6; contains_cases(); signalled = 7; hasprefix_cases(); signalled = 8; hassuffix_cases(); signalled = 9; tokenize_cases(); signalled = 10; rtokenize_cases(); signalled = 11; peek_token_cases(); signalled = 12; remaining_tokens_cases(); return 0; };