lib/memio: fixedwrite returns nomem on full buffer (F-R)

memio.fixedwrite returned a successful 0-byte write once the sink
filled, so an overflowing fprintf/bsprintf surfaced a truncated prefix
as a successful str instead of an error. Hare's fixed_write returns
nomem there (ref/hare/memio/stream.ha:161); the bsprintf/fprintf
io.error arm already forwards it, so the prefix-on-overflow path is the
only divergence.

Mirror Hare's full guard order: an empty input buf short-circuits to 0
(stream.ha:157) before the full-sink nomem guard, so a 0-byte write to
a full sink stays 0 (no new divergence). fmt.bsprintf/formatone keep
their logic; only their now-stale WHY-comments are rewritten, and
formatone's tail-pad counter is left as-is (the width-form restore is a
deferred follow-up, out of F-R scope). memio's own `fixed` doc comment,
which still claimed ww surfaces 0 on a full buffer, is corrected to the
new nomem contract.

Tests: flip the two fmt rows that pinned the prefix bug (bsprintf_trunc,
bsprintf_width_trunc) plus memiotest fixedwritecases' overflow row to
assert `is nomem`; add positive controls (bsprintf_exact must still
succeed) + an empty-sink discriminator (bsprintf_empty) + a dedicated
fixedwritefull unit pinning the memio.ww:190 contract.

Regenerates the w6c and wwdump combined.ww (memio's fixedwrite change
and `fixed` doc comment are the only embedded changes; fmt is
dead-code-eliminated from both).
This commit is contained in:
2026-06-14 23:52:15 +09:00
parent 46ed712352
commit cab85f5bc9
6 changed files with 128 additions and 45 deletions

View File

@@ -17591,10 +17591,9 @@ export type stream = struct {
};
// fixed — wire a stream over a caller-supplied buffer. Writes never
// grow; they return 0 once `pos` reaches the end of the buffer (Hare
// returns `nomem` here; ww surfaces 0 — graduating to Hare's `nomem`
// return needs the widen-from-bare-nomem path that #173-family work
// gates).
// grow; a write to a full buffer returns nomem, matching Hare
// (ref/hare/memio/stream.ha:44,161). A zero-length write always
// succeeds with 0 (the empty-input short-circuit, stream.ha:157).
//
// Mirrors ref/hare/memio/stream.ha:46.
export fn fixed(buf: []u8) stream = {
@@ -17708,7 +17707,12 @@ fn seekfn(s: io.stream, off: io.off, w: io.whence) (io.off | io.error) = {
fn fixedwrite(s: io.stream, buf: []u8) (size | io.error) = {
let m: *stream = s: *stream;
if (m.pos >= m.len) { return 0: size; };
if (buf.len == 0) { return 0: size; }; // ref/hare/memio/stream.ha:157
if (m.pos >= m.len) { // ref/hare/memio/stream.ha:161
let nm: nomem;
let e: io.error = nm;
return e;
};
let space: i32 = m.len - m.pos;
let n: i32 = buf.len;
if (space < n) { n = space; };