// strings — operations over the immutable str type ({ *u8, len }). // Mirrors Hare's strings::; `len` and `is-empty` aren't functions // (callers use `s.len` and `s.len == 0` directly). use os; // compare — bytewise three-way comparison: negative if ab. Matches Hare's strings::compare. ASCII-order, not // locale-aware. Callers that just need equality use `compare(a, b) == 0`. export fn compare(a: str, b: str) i32 = { let n: i32 = a.len; if (b.len < n) { n = b.len; }; let i: i32 = 0; for (i < n) { if (a[i] != b[i]) { return (a[i]: i32) - (b[i]: i32); }; i += 1; }; return a.len - b.len; }; export fn hasprefix(s: str, p: str) bool = { if (p.len > s.len) { return false; }; let i: i32 = 0; for (i < p.len) { if (s[i] != p[i]) { return false; }; i += 1; }; return true; }; export fn hassuffix(s: str, suf: str) bool = { if (suf.len > s.len) { return false; }; let off: i32 = s.len - suf.len; let i: i32 = 0; for (i < suf.len) { if (s[off + i] != suf[i]) { return false; }; i += 1; }; return true; }; // byteindex — first index of byte `c` in `s`, or -1 if absent. Hare // name (strings::byteindex). Plan 9-style sentinel return; callers that // prefer a fallible shape can wrap this in their own (i32 | str). No // allocation. export fn byteindex(s: str, c: u8) i32 = { let i: i32 = 0; for (i < s.len) { if (s[i] == c) { return i; }; i += 1; }; return -1; }; // rbyteindex — last index of byte `c` in `s`, or -1 if absent. Mirrors // Hare's strings::rbyteindex. export fn rbyteindex(s: str, c: u8) i32 = { let i: i32 = s.len - 1; for (i >= 0) { if (s[i] == c) { return i; }; i -= 1; }; return -1; }; // index — first index of `sub` in `s`, or -1. Naive scan; fine for // short patterns and small strings, which dominate config and CLI // parsing. Empty `sub` matches at 0. export fn index(s: str, sub: str) i32 = { if (sub.len == 0) { return 0; }; if (sub.len > s.len) { return -1; }; let last: i32 = s.len - sub.len; let i: i32 = 0; for (i <= last) { let j: i32 = 0; let ok: bool = true; for (j < sub.len) { if (s[i + j] != sub[j]) { ok = false; j = sub.len; } else { j += 1; }; }; if (ok) { return i; }; i += 1; }; return -1; }; export fn contains(s: str, sub: str) bool = { return index(s, sub) >= 0; }; // concat — joins two strings into a fresh str. Caller owns the // returned str's storage; release via `os.free(r.ptr, r.len)`. Mirrors // Hare's strings::concat shape. export fn concat(a: str, b: str) str = { let total: i32 = a.len + b.len; let buf: *u8 = os.alloc(total: u64): *u8; let i: i32 = 0; for (i < a.len) { buf[i] = a[i]; i += 1; }; let j: i32 = 0; for (j < b.len) { buf[a.len + j] = b[j]; j += 1; }; let r: str; r.ptr = buf; r.len = total; return r; };