bufio: bclose flushes only, does not close the underlying (Hare flag::NONE)
This commit is contained in:
@@ -46,8 +46,10 @@
|
|||||||
// singletons (vtable_r / vtable_w / vtable_rw); ww's vtable always
|
// singletons (vtable_r / vtable_w / vtable_rw); ww's vtable always
|
||||||
// carries all three callbacks (a zero-length rbuf/wbuf degenerates the
|
// carries all three callbacks (a zero-length rbuf/wbuf degenerates the
|
||||||
// matching callback in-cb). Defer-handle (MANAGED_* ownership bits)
|
// matching callback in-cb). Defer-handle (MANAGED_* ownership bits)
|
||||||
// also deferred — caller owns rbuf/wbuf/src; bclose flushes + forwards
|
// also deferred — caller owns rbuf/wbuf/src; bclose flushes only and
|
||||||
// close but frees nothing. Both graduate with io fold-2 (#5).
|
// neither frees nor closes (close-propagation rides #5
|
||||||
|
// MANAGED_HANDLE, ref/hare/bufio/stream.ha:194). Both graduate with io
|
||||||
|
// fold-2 (#5).
|
||||||
|
|
||||||
package bufio;
|
package bufio;
|
||||||
|
|
||||||
@@ -292,20 +294,15 @@ fn bwrite(s: io.stream, buf: []u8) (size | io.error) = {
|
|||||||
return buf.len: size;
|
return buf.len: size;
|
||||||
};
|
};
|
||||||
|
|
||||||
// bclose — flush pending wbuf, forward close to src via io.close.
|
// bclose — flush pending wbuf only; the underlying src is NOT closed.
|
||||||
// Caller-owned buffers/src are not freed (drew defer-handle deferral).
|
// Hare's close_buffered closes src solely under flag::MANAGED_HANDLE
|
||||||
|
// (ref/hare/bufio/stream.ha:194); init's default flag::NONE
|
||||||
|
// (stream.ha:73) flushes and leaves src to its owner. Close-propagation
|
||||||
|
// rides the io fold-2 MANAGED_HANDLE machinery (#5), per the ownership
|
||||||
|
// header (bufio.ww:46-50) — caller owns src, bclose frees/closes nothing.
|
||||||
fn bclose(s: io.stream) (void | io.error) = {
|
fn bclose(s: io.stream) (void | io.error) = {
|
||||||
let b: *stream = s: *stream;
|
let b: *stream = s: *stream;
|
||||||
let fr: (void | io.error) = flush(b);
|
return flush(b);
|
||||||
match (fr) {
|
|
||||||
case void => { };
|
|
||||||
case let e: io.error => return e;
|
|
||||||
};
|
|
||||||
let cr: (void | io.error) = io.close(b.src);
|
|
||||||
match (cr) {
|
|
||||||
case void => return void;
|
|
||||||
case let e: io.error => return e;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---- scanner (read-ahead tokenizer over an io.stream src) ----------------
|
// ---- scanner (read-ahead tokenizer over an io.stream src) ----------------
|
||||||
|
|||||||
@@ -43,6 +43,27 @@ fn errsource() io.stream = {
|
|||||||
return &errvt;
|
return &errvt;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ---- a close-recording source for the no-propagate test ---------------
|
||||||
|
//
|
||||||
|
// closevt forwards writes to closesink (so a flush is observable) and
|
||||||
|
// records whether its closer fired in srcclosed.
|
||||||
|
let srcclosed: bool = false;
|
||||||
|
let closevt: io.vtable;
|
||||||
|
let closesink: memio.stream;
|
||||||
|
|
||||||
|
fn closewrite(s: io.stream, buf: []u8) (size | io.error) = {
|
||||||
|
return io.write(&closesink.vt, buf);
|
||||||
|
};
|
||||||
|
fn closeclose(s: io.stream) (void | io.error) = {
|
||||||
|
srcclosed = true;
|
||||||
|
return void;
|
||||||
|
};
|
||||||
|
fn closesource() io.stream = {
|
||||||
|
closevt.writer = (&closewrite): *io.writer;
|
||||||
|
closevt.closer = (&closeclose): *io.closer;
|
||||||
|
return &closevt;
|
||||||
|
};
|
||||||
|
|
||||||
// ---- scanbyte: drain four bytes through a 2-byte window ---------------
|
// ---- scanbyte: drain four bytes through a 2-byte window ---------------
|
||||||
|
|
||||||
@test fn scanbytecases() void = {
|
@test fn scanbytecases() void = {
|
||||||
@@ -732,6 +753,47 @@ fn errsource() io.stream = {
|
|||||||
assert(!(raw[1] != 81u8));
|
assert(!(raw[1] != 81u8));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ---- bclose flushes but does NOT close the underlying ----------------
|
||||||
|
//
|
||||||
|
// Hare's close_buffered closes src only under flag::MANAGED_HANDLE
|
||||||
|
// (ref/hare/bufio/stream.ha:194); init's default flag::NONE flushes
|
||||||
|
// only. The close-recording closesource lets us assert both: the
|
||||||
|
// buffered byte reaches the sink (flush) AND the closer never fires.
|
||||||
|
@test fn streamclosenopropagate() void = {
|
||||||
|
srcclosed = false;
|
||||||
|
let raw: [8]u8;
|
||||||
|
closesink = memio.fixed(raw[0:8]);
|
||||||
|
let src: io.stream = closesource();
|
||||||
|
|
||||||
|
let rb: [4]u8;
|
||||||
|
let wb: [4]u8;
|
||||||
|
let b: bufio.stream = bufio.init(src, rb[0:4], wb[0:4]);
|
||||||
|
let nilbs: [1]u8;
|
||||||
|
bufio.setflush(&b, nilbs[0:0]); // suppress the default '\n' detector
|
||||||
|
|
||||||
|
let p: io.stream = &b.vt;
|
||||||
|
let h: [2]u8;
|
||||||
|
h[0] = 80u8; h[1] = 81u8; // "PQ"
|
||||||
|
let r: (size | io.error) = io.write(p, h[0:2]);
|
||||||
|
match (r) {
|
||||||
|
case let n: size => { assert(!(n: i32 != 2)); };
|
||||||
|
case let e: io.error => abort();
|
||||||
|
};
|
||||||
|
assert(!(closesink.pos != 0)); // still buffered
|
||||||
|
|
||||||
|
let cr: (void | io.error) = io.close(p);
|
||||||
|
match (cr) {
|
||||||
|
case void => { };
|
||||||
|
case let e: io.error => abort();
|
||||||
|
};
|
||||||
|
// Flush happened: the 2 buffered bytes reached the sink.
|
||||||
|
assert(!(closesink.pos != 2));
|
||||||
|
assert(!(raw[0] != 80u8));
|
||||||
|
assert(!(raw[1] != 81u8));
|
||||||
|
// But the underlying was NOT closed (Hare flag::NONE default).
|
||||||
|
assert(!srcclosed);
|
||||||
|
};
|
||||||
|
|
||||||
// ---- setflush with a custom non-empty byte-set -----------------------
|
// ---- setflush with a custom non-empty byte-set -----------------------
|
||||||
|
|
||||||
@test fn streamsetflushcustom() void = {
|
@test fn streamsetflushcustom() void = {
|
||||||
|
|||||||
Reference in New Issue
Block a user