// bufio — buffered reader/writer over io.stream. Plan 9 'bio' // analogue, lowered to ww. The buffer is owned by the caller and // passed in at init time; we don't allocate. // // We keep the stream pointer as *void here to avoid a cross-module // type name (the module-import system isn't online yet); a later // revision will replace it with *io.stream once `use` resolves // types from imported modules. type streamp = *void; type buf = struct { s: streamp, data: *u8, cap: i32, r: i32, // read cursor w: i32, // write cursor (for writers) }; export fn rinit(b: *buf, s: streamp, data: *u8, cap: i32) void = { b.s = s; b.data = data; b.cap = cap; b.r = 0; b.w = 0; }; // peek1 — look at the next byte without consuming. Returns -1 on // empty buffer; the caller is responsible for refilling via the // stream when this happens. export fn peek1(b: *buf) i32 = { if (b.r < b.w) { return b.data[b.r]: i32; }; return -1; }; // take1 — pop one byte. -1 if empty. export fn take1(b: *buf) i32 = { if (b.r < b.w) { let c: u8 = b.data[b.r]; b.r += 1; return c: i32; }; return -1; }; // avail — bytes left to read out of the buffer. export fn avail(b: *buf) i32 = { return b.w - b.r; }; // Distinct alias so `(str | linerr)` has two variant types the // tagged-union machinery can keep apart at the tag level. The error // variant carries a short description; callers compare with errors.is // or just inspect by length. type linerr = str; // takeline — Hare-style fallible line read. Drains the buffer up to // (but not including) the next '\n' and advances the cursor past the // newline. Returns the line as a borrowed str on success, or a linerr // describing why no line was available: // - "eof" when the buffer is empty // - "no newline" when the buffer contains data but no '\n' // // The returned str borrows from the underlying buffer; callers must // consume it (or copy) before refilling. export fn takeline(b: *buf) (str | linerr) = { if (b.r >= b.w) { return "eof": linerr; }; let i: i32 = b.r; for (i < b.w) { if (b.data[i] == 10u8) { let s: str; s.ptr = b.data + b.r; s.len = i - b.r; b.r = i + 1; return s; }; i += 1; }; return "no newline": linerr; };