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:
@@ -70,10 +70,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 = {
|
||||
@@ -187,7 +186,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; };
|
||||
|
||||
@@ -85,15 +85,17 @@ fn putstr(s: str, into: []u8, off: i32) i32 = {
|
||||
let src: [32]u8;
|
||||
let _: i32 = putstr("hello world!!XXXXXXXY", src[0:32], 0);
|
||||
|
||||
// (srcoff, inlen, wantn) — wantn diverges from inlen on the
|
||||
// partial and overflow rows.
|
||||
let off: [4]i32;
|
||||
let ln: [4]i32;
|
||||
let wantn: [4]i32;
|
||||
off[0]=0; ln[0]=6; wantn[0]=6; // full fit
|
||||
off[1]=6; ln[1]=7; wantn[1]=7; // exact-fills cap (pos=13)
|
||||
off[2]=13; ln[2]=7; wantn[2]=3; // partial: 3 free
|
||||
off[3]=20; ln[3]=1; wantn[3]=0; // overflow: 0 free → 0
|
||||
// (srcoff, inlen, wantn, wantnomem) — wantn diverges from inlen on
|
||||
// the partial row; the overflow row writes into a now-full sink and
|
||||
// returns nomem (ref/hare/memio/stream.ha:161).
|
||||
let off: [4]i32;
|
||||
let ln: [4]i32;
|
||||
let wantn: [4]i32;
|
||||
let wantnomem: [4]bool;
|
||||
off[0]=0; ln[0]=6; wantn[0]=6; wantnomem[0]=false; // full fit
|
||||
off[1]=6; ln[1]=7; wantn[1]=7; wantnomem[1]=false; // exact-fills cap (pos=13)
|
||||
off[2]=13; ln[2]=7; wantn[2]=3; wantnomem[2]=false; // partial: 3 free
|
||||
off[3]=20; ln[3]=1; wantn[3]=0; wantnomem[3]=true; // overflow: full sink → nomem
|
||||
|
||||
let i: i32 = 0;
|
||||
for (i < 4) {
|
||||
@@ -101,8 +103,14 @@ fn putstr(s: str, into: []u8, off: i32) i32 = {
|
||||
let hi: i32 = lo + ln[i];
|
||||
let r: (size | io.error) = io.write(s, src[lo:hi]);
|
||||
match (r) {
|
||||
case let n: size => { assert(!(n: i32 != wantn[i])); };
|
||||
case let e: io.error => abort();
|
||||
case let n: size => {
|
||||
assert(!wantnomem[i]);
|
||||
assert(!(n: i32 != wantn[i]));
|
||||
};
|
||||
case let e: io.error => {
|
||||
assert(wantnomem[i]);
|
||||
assert(e is nomem);
|
||||
};
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
@@ -122,6 +130,41 @@ fn putstr(s: str, into: []u8, off: i32) i32 = {
|
||||
match (c) { case void => {}; case let e: io.error => abort(); };
|
||||
};
|
||||
|
||||
// ---- fixedwritefull: the full-sink nomem contract (memio.ww:190) -------
|
||||
// A write that exactly fills the sink succeeds; any further non-empty
|
||||
// write into the full sink returns nomem, not a 0-byte success
|
||||
// (ref/hare/memio/stream.ha:161). A zero-length sink is full from the
|
||||
// start, so its first non-empty write is already nomem.
|
||||
|
||||
@test fn fixedwritefull() void = {
|
||||
let buf: [4]u8;
|
||||
let st: memio.stream = memio.fixed(buf[0:4]);
|
||||
let s: io.stream = &st.vt;
|
||||
|
||||
let src: [4]u8;
|
||||
let _: i32 = putstr("abcd", src[0:4], 0);
|
||||
|
||||
let r4: (size | io.error) = io.write(s, src[0:4]); // exact fill
|
||||
match (r4) {
|
||||
case let n: size => { assert(!(n != 4: size)); };
|
||||
case let e: io.error => abort();
|
||||
};
|
||||
|
||||
let r1: (size | io.error) = io.write(s, src[0:1]); // full sink
|
||||
match (r1) {
|
||||
case let n: size => abort();
|
||||
case let e: io.error => { assert(e is nomem); };
|
||||
};
|
||||
|
||||
let ze: memio.stream = memio.fixed(buf[0:0]);
|
||||
let zs: io.stream = &ze.vt;
|
||||
let rz: (size | io.error) = io.write(zs, src[0:1]); // zero-len sink
|
||||
match (rz) {
|
||||
case let n: size => abort();
|
||||
case let e: io.error => { assert(e is nomem); };
|
||||
};
|
||||
};
|
||||
|
||||
// ---- dynamicgrowcases: every cap doubling exercised ---------------------
|
||||
|
||||
// Drive grow 0 → 8 → 16 → 32 by writing sized chunks. Verify
|
||||
|
||||
Reference in New Issue
Block a user