lib/strings+test: graduate fromutf8 + bytesub to validating return
fromutf8(in: []u8) (str | utf8.invalid) and the bytesub form per ref/hare/strings/utf8.ha:22 and sub.ha:59. bytesub keeps its byte asserts (ww extension over Hare; predates #7). fromutf8 walks the utf8 decoder via utf8.next rather than the shorter `utf8.validate(in)?` form. Two compiler bugs in the way: cross-shape `(void | invalid) → (str | invalid)` propagation is #19, and (void | !void) match-bind locals diverge between stages / str→union lift SIGSEGVs in cstage — both filed as #48. The decoder-walk form bypasses both and matches what ref/hare/strings/utf8.ha actually does in source. getopt.ww:314 caller updated to match the new (str | invalid) return; bi+1 cannot hit a continuation byte in well-formed argv (bi is a just-matched ASCII flag), so abort spells the precondition. bytesub_cases rewritten as exhaustive match; new rows cover start-on-continuation and end-on-continuation invalid arms plus an end==s.len bypass. fromutf8_cases is new — Hare vector + edge bytes + multibyte parity rows.
This commit is contained in:
@@ -1885,6 +1885,39 @@ export fn fromutf8_unsafe(in: []u8) str = {
|
||||
return r;
|
||||
};
|
||||
|
||||
// fromutf8 — validating sibling of [[fromutf8_unsafe]]. Returns the
|
||||
// borrowed str view on success, or `utf8.invalid` if `in` contains a
|
||||
// malformed UTF-8 sequence. ref/hare/strings/utf8.ha:22 (#7).
|
||||
//
|
||||
// Hare's spelling is `utf8::validate(in)?; return fromutf8_unsafe(in)`.
|
||||
// ww calls `utf8.validate` and matches its `(void | utf8.invalid)`
|
||||
// result instead of `?` (cross-shape propagation is #19). Even with
|
||||
// match, the call diverges cstage/wwstage when the result is bound to
|
||||
// a local (#48: wwstage spills DX for the payload-less arm) and the
|
||||
// success path then SIGSEGVs lifting `str` to `(str | utf8.invalid)`.
|
||||
// So the validation is open-coded against the decoder — the same
|
||||
// byte-by-byte DFA walk `utf8.validate` performs (ref/hare/encoding/
|
||||
// utf8/decode.ha:207) — whose `(rune | done | more | invalid)` arm
|
||||
// matches the failure axis of fromutf8 and lifts cleanly. Collapses
|
||||
// to `utf8.validate(in)?` once #19 + #48 are fixed.
|
||||
export fn fromutf8(in: []u8) (str | utf8.invalid) = {
|
||||
let d: utf8.decoder = utf8.decode(in);
|
||||
for (true) {
|
||||
match (utf8.next(&d)) {
|
||||
case let r: rune => void;
|
||||
case utf8.done => {
|
||||
let r: str = fromutf8_unsafe(in);
|
||||
return r;
|
||||
};
|
||||
case utf8.more => {
|
||||
let e: utf8.invalid; return e;
|
||||
};
|
||||
case let e: utf8.invalid => { return e; };
|
||||
};
|
||||
};
|
||||
let e: utf8.invalid; return e;
|
||||
};
|
||||
|
||||
// compare — three-way bytewise codepoint-order comparison. Return is
|
||||
// a sign (neg/zero/pos), not an index, so it tracks Hare's `int`
|
||||
// rather than the str-index i32 (#8). ref/hare/strings/compare.ha:12.
|
||||
@@ -2072,12 +2105,23 @@ export fn sub(s: str, start: i32, end: i32) str = {
|
||||
};
|
||||
|
||||
// bytesub — borrowed substring [start, end) where start/end are byte
|
||||
// offsets. ref/hare/strings/sub.ha:59. Hare's validating return
|
||||
// `(str | utf8::invalid)` is dropped (filed as #7); a multibyte-split
|
||||
// argument will yield a sub-string whose bytes don't decode cleanly.
|
||||
export fn bytesub(s: str, start: i32, end: i32) str = {
|
||||
// offsets. ref/hare/strings/sub.ha:59 (#7). Returns `utf8.invalid` if
|
||||
// either endpoint lands on a continuation byte (would split a
|
||||
// codepoint); the equivalent Hare predicate is `s[i] & 0xc0 == 0x80`
|
||||
// at ref/hare/strings/sub.ha:72-73.
|
||||
export fn bytesub(s: str, start: i32, end: i32) (str | utf8.invalid) = {
|
||||
os.assert(start <= end, "strings.bytesub: start is higher than end");
|
||||
os.assert(end <= s.len, "strings.bytesub: end exceeds string length");
|
||||
if (start < s.len) {
|
||||
if ((s[start] & 0xC0u8) == 0x80u8) {
|
||||
let e: utf8.invalid; return e;
|
||||
};
|
||||
};
|
||||
if (end < s.len) {
|
||||
if ((s[end] & 0xC0u8) == 0x80u8) {
|
||||
let e: utf8.invalid; return e;
|
||||
};
|
||||
};
|
||||
let r: str;
|
||||
r.ptr = s.ptr + (start: u64);
|
||||
r.len = end - start;
|
||||
|
||||
Reference in New Issue
Block a user