hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit b9fdbbc5f173bac432fd716792304b7797465813
parent fba5c36a64ffcf04660dc7fe0449bb267a783ea0
Author: Alexey Yerin <yyp@disroot.org>
Date:   Sun,  2 Jan 2022 17:18:33 +0300

bufio: use caller allocation

fixed() and dynamic() return a memstream, and buffered() returns
bufstream. This also removes the need for static_buffered() hack.

Similarly to strio, bufio::finish() is removed as the streams do not
allocate themselves.

Necessary update is adding '&' to uses of bufio streams.

Signed-off-by: Alexey Yerin <yyp@disroot.org>

Diffstat:
Mbufio/buffered.ha | 91+++++++++++++++++++++++++++++--------------------------------------------------
Mbufio/memstream.ha | 155++++++++++++++++++++++++++++++++-----------------------------------------------
Mcmd/hare/subcmds.ha | 2+-
Mcmd/harec/context.ha | 3++-
Mcmd/harec/gen.ha | 22+++++++++++-----------
Mcmd/harec/main.ha | 4++--
Mcmd/haredoc/docstr.ha | 38+++++++++++++++++++-------------------
Mcmd/haredoc/html.ha | 2+-
Mcrypto/argon2/argon2.ha | 11+++++------
Mencoding/base64/base64.ha | 15++++++---------
Mfmt/fmt.ha | 14++++++--------
Mformat/xml/parser.ha | 9+++++----
Mformat/xml/types.ha | 3++-
Mfs/mem/stream.ha | 20++++++++++----------
Mhare/module/manifest.ha | 2+-
Mhare/parse/ident.ha | 3+--
Mos/+linux/stdfd.ha | 10++++++----
Mpath/join.ha | 6+++---
Munix/hosts/lookup.ha | 6++----
Munix/resolvconf/load.ha | 6++----
Muuid/uuid.ha | 3+--
21 files changed, 182 insertions(+), 243 deletions(-)

diff --git a/bufio/buffered.ha b/bufio/buffered.ha @@ -34,21 +34,9 @@ export fn buffered( src: io::handle, rbuf: []u8, wbuf: []u8, -) io::handle = { - let s = alloc(bufstream { ... }); - let st = static_buffered(src, rbuf, wbuf, s); - st.closer = &buffered_close; - return st; -}; - -export fn static_buffered( - src: io::handle, - rbuf: []u8, - wbuf: []u8, - s: *bufstream, -) *io::stream = { +) bufstream = { static let flush_default = ['\n': u32: u8]; - *s = bufstream { + let s = bufstream { stream = io::stream { closer = &buffered_close_static, ... @@ -69,7 +57,7 @@ export fn static_buffered( assert(rbuf: *[*]u8 != wbuf: *[*]u8, "Cannot use bufio::buffered with same buffer for reads and writes"); }; - return &s.stream; + return s; }; fn getstream(in: io::handle) *bufstream = { @@ -78,8 +66,7 @@ fn getstream(in: io::handle) *bufstream = { }; // Flushes pending writes to the underlying stream. -export fn flush(s: io::handle) (io::error | void) = { - let s = getstream(s); +export fn flush(s: *bufstream) (io::error | void) = { if (s.wavail == 0) { return; }; @@ -90,22 +77,19 @@ export fn flush(s: io::handle) (io::error | void) = { // Sets the list of bytes which will cause the stream to flush when written. By // default, the stream will flush when a newline (\n) is written. -export fn setflush(s: io::handle, b: []u8) void = { - let s = getstream(s); +export fn setflush(s: *bufstream, b: []u8) void = { s.flush = b; }; // Unreads a slice of bytes. The next read calls on this buffer will consume the // un-read data before consuming further data from the underlying source, or any // buffered data. -export fn unread(s: io::handle, buf: []u8) void = { - let s = getstream(s); +export fn unread(s: *bufstream, buf: []u8) void = { append(s.unread, buf...); }; // Unreads a rune; see [[unread]]. -export fn unreadrune(s: io::handle, rn: rune) void = { - let s = getstream(s); +export fn unreadrune(s: *bufstream, rn: rune) void = { const buf = utf8::encoderune(rn); insert(s.unread[0], buf...); }; @@ -120,18 +104,10 @@ export fn isbuffered(in: io::handle) bool = { }; }; -fn buffered_close(s: *io::stream) void = { - assert(s.closer == &buffered_close); - if (s.writer != null) { - flush(s)!; - }; - free(s); -}; - fn buffered_close_static(s: *io::stream) void = { assert(s.closer == &buffered_close_static); if (s.writer != null) { - flush(s)!; + flush(s: *bufstream)!; }; }; @@ -188,7 +164,7 @@ fn buffered_write(s: *io::stream, buf: const []u8) (size | io::error) = { for (len(buf) > 0) { let avail = len(s.wbuffer) - s.wavail; if (avail == 0) { - flush(&s.stream)?; + flush(s)?; avail = len(s.wbuffer); }; @@ -200,7 +176,7 @@ fn buffered_write(s: *io::stream, buf: const []u8) (size | io::error) = { }; if (doflush) { - flush(&s.stream)?; + flush(s)?; }; return z; @@ -209,54 +185,53 @@ fn buffered_write(s: *io::stream, buf: const []u8) (size | io::error) = { @test fn buffered_read() void = { let sourcebuf: []u8 = [1, 3, 3, 7]; let source = fixed(sourcebuf, io::mode::READ); - let fb = source: *memstream; - defer io::close(source); + defer io::close(&source); let rbuf: [1024]u8 = [0...]; let f = buffered(source, rbuf, []); - defer io::close(f); + defer io::close(&f); let buf: [1024]u8 = [0...]; - assert(io::read(f, buf[..2]) as size == 2); - assert(fb.pos == len(fb.buf), "fixed stream was not fully consumed"); + assert(io::read(&f, buf[..2]) as size == 2); + assert(source.pos == len(source.buf), "fixed stream was not fully consumed"); assert(bytes::equal(buf[..2], [1, 3])); - assert(io::read(f, buf[2..]) as size == 2); + assert(io::read(&f, buf[2..]) as size == 2); assert(bytes::equal(buf[..4], [1, 3, 3, 7])); - assert(io::read(f, buf) is io::EOF); + assert(io::read(&f, buf) is io::EOF); let sourcebuf: [32]u8 = [1, 3, 3, 7, 0...]; let source = fixed(sourcebuf, io::mode::READ); - let fb = source: *memstream; + let source = source: *memstream; defer io::close(source); let rbuf: [16]u8 = [0...]; let f = buffered(source, rbuf, []); - defer io::close(f); + defer io::close(&f); let buf: [32]u8 = [0...]; - assert(io::read(f, buf) as size == 16); - assert(fb.pos == 16); + assert(io::read(&f, buf) as size == 16); + assert(source.pos == 16); - assert(io::read(f, buf[16..]) as size == 16); + assert(io::read(&f, buf[16..]) as size == 16); assert(bytes::equal(buf, sourcebuf)); - assert(io::read(f, buf) is io::EOF); - assert(fb.pos == len(fb.buf)); + assert(io::read(&f, buf) is io::EOF); + assert(source.pos == len(source.buf)); }; @test fn buffered_write() void = { // Normal case let sink = dynamic(io::mode::WRITE); - defer io::close(sink); + defer io::close(&sink); let wbuf: [1024]u8 = [0...]; let f = buffered(sink, [], wbuf); - defer io::close(f); + defer io::close(&f); - assert(io::write(f, [1, 3, 3, 7]) as size == 4); + assert(io::write(&f, [1, 3, 3, 7]) as size == 4); assert(len(buffer(sink)) == 0); - assert(io::write(f, [1, 3, 3, 7]) as size == 4); - assert(flush(f) is void); + assert(io::write(&f, [1, 3, 3, 7]) as size == 4); + assert(flush(&f) is void); assert(bytes::equal(buffer(sink), [1, 3, 3, 7, 1, 3, 3, 7])); // Test flushing via buffer exhaustion @@ -266,11 +241,11 @@ fn buffered_write(s: *io::stream, buf: const []u8) (size | io::error) = { let wbuf: [4]u8 = [0...]; let f = buffered(sink, [], wbuf); - assert(io::write(f, [1, 3, 3, 7]) as size == 4); + assert(io::write(&f, [1, 3, 3, 7]) as size == 4); assert(len(buffer(sink)) == 0); - assert(io::write(f, [1, 3, 3, 7]) as size == 4); + assert(io::write(&f, [1, 3, 3, 7]) as size == 4); assert(bytes::equal(buffer(sink), [1, 3, 3, 7])); - io::close(f); // Should flush + io::close(&f); // Should flush assert(bytes::equal(buffer(sink), [1, 3, 3, 7, 1, 3, 3, 7])); // Test flushing via flush characters @@ -280,8 +255,8 @@ fn buffered_write(s: *io::stream, buf: const []u8) (size | io::error) = { let wbuf: [1024]u8 = [0...]; let f = buffered(sink, [], wbuf); - assert(io::write(f, strings::toutf8("hello")) as size == 5); + assert(io::write(&f, strings::toutf8("hello")) as size == 5); assert(len(buffer(sink)) == 0); - assert(io::write(f, strings::toutf8(" world!\n")) as size == 8); + assert(io::write(&f, strings::toutf8(" world!\n")) as size == 8); assert(bytes::equal(buffer(sink), strings::toutf8("hello world!\n"))); }; diff --git a/bufio/memstream.ha b/bufio/memstream.ha @@ -3,24 +3,22 @@ use io; use strings; use errors; -type memstream = struct { +export type memstream = struct { stream: io::stream, buf: []u8, pos: size, }; -// Creates an [[io::stream]] for a fixed, caller-supplied buffer. Supports +// Creates a stream for a fixed, caller-supplied buffer. Supports // either read or write, but not both. Readable streams are seekable. The -// program aborts if writes would exceed the buffer's capacity. -export fn fixed(in: []u8, mode: io::mode) *io::stream = { - let s = alloc(memstream { - stream = io::stream { - closer = &fixed_close, - ... - }, +// program aborts if writes would exceed the buffer's capacity. The stream +// doesn't have to be closed. +export fn fixed(in: []u8, mode: io::mode) memstream = { + let s = memstream { + stream = io::stream { ... }, buf = in, pos = 0, - }); + }; if (mode & io::mode::READ == io::mode::READ) { assert(mode & io::mode::WRITE != io::mode::WRITE); s.stream.reader = &read; @@ -30,7 +28,7 @@ export fn fixed(in: []u8, mode: io::mode) *io::stream = { assert(mode & io::mode::READ != io::mode::READ); s.stream.writer = &fixed_write; }; - return &s.stream; + return s; }; fn fixed_write(s: *io::stream, buf: const []u8) (size | io::error) = { @@ -45,24 +43,20 @@ fn fixed_write(s: *io::stream, buf: const []u8) (size | io::error) = { return n; }; -fn fixed_close(s: *io::stream) void = free(s); - // Creates an [[io::stream]] which dynamically allocates a buffer to store writes // into. Subsequent reads will consume the buffered data. Upon failure to // allocate sufficient memory to store writes, the program aborts. // -// Calling [[io::close]] on this stream will free the buffer. Call -// [[bufio::finish]] instead to free up resources associated with the stream, -// but transfer ownership of the buffer to the caller. -export fn dynamic(mode: io::mode) *io::stream = dynamic_from([], mode); +// Calling [[io::close]] on this stream will free the buffer. If stream's +// data is transferred via [[buffer]], it shouldn't be closed as long as the +// data is freed. +export fn dynamic(mode: io::mode) memstream = dynamic_from([], mode); // Like [[dynamic]], but takes an existing slice as input. Writes are appended // to it and reads consume bytes from the initial buffer, plus any additional -// writes. Like [[dynamic]], calling [[io::close]] will free the buffer, and -// [[bufio::finish]] can be used to return ownership of the buffer to the -// caller. -export fn dynamic_from(in: []u8, mode: io::mode) *io::stream = { - let s = alloc(memstream { +// writes. Like [[dynamic]], calling [[io::close]] will free the buffer. +export fn dynamic_from(in: []u8, mode: io::mode) memstream = { + let s = memstream { stream = io::stream { closer = &dynamic_close, seeker = &seek, @@ -70,55 +64,33 @@ export fn dynamic_from(in: []u8, mode: io::mode) *io::stream = { }, buf = in, pos = 0, - }): *io::stream; + }; if (mode & io::mode::READ == io::mode::READ) { - s.reader = &read; + s.stream.reader = &read; }; if (mode & io::mode::WRITE == io::mode::WRITE) { - s.writer = &dynamic_write; + s.stream.writer = &dynamic_write; }; return s; }; -fn getmemstream(in: io::handle) *memstream = { - match (in) { - case io::file => - abort("Invalid use of bufio with unbuffered file"); - case let st: *io::stream => - assert(st.closer == &dynamic_close); - return st: *memstream; - }; -}; - -// Closes the stream without freeing the dynamic buffer, instead transferring -// ownership of it to the caller. -export fn finish(in: io::handle) []u8 = { - let s = getmemstream(in); - let buf = s.buf; - free(s); - return buf; -}; - -// Returns the current buffer. -export fn buffer(in: io::handle) []u8 = { - const s = getmemstream(in); - return s.buf; +// Returns the current buffer of a [[fixed]] or [[dynamic]] stream. +export fn buffer(in: *memstream) []u8 = { + return in.buf; }; // Resets the dynamic buffer's length to zero, but keeps the allocated memory // around for future writes. -export fn reset(in: io::handle) void = { - const s = getmemstream(in); - s.pos = 0; - s.buf = s.buf[..0]; +export fn reset(in: *memstream) void = { + in.pos = 0; + in.buf = in.buf[..0]; }; // Truncates the dynamic buffer, freeing memory associated with it and setting // its length to zero. -export fn truncate(in: io::handle) (void | errors::unsupported) = { - let s = getmemstream(in); - s.pos = 0; - delete(s.buf[..]); +export fn truncate(in: *memstream) (void | errors::unsupported) = { + in.pos = 0; + delete(in.buf[..]); }; fn dynamic_write(s: *io::stream, buf: const []u8) (size | io::error) = { @@ -131,7 +103,6 @@ fn dynamic_write(s: *io::stream, buf: const []u8) (size | io::error) = { fn dynamic_close(s: *io::stream) void = { const s = s: *memstream; free(s.buf); - free(s); }; fn read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = { @@ -178,59 +149,59 @@ fn seek( @test fn dynamic() void = { let s = dynamic(io::mode::RDWR); - assert(io::write(s, [1, 2, 3]) as size == 3); - assert(bytes::equal(buffer(s), [1, 2, 3])); - assert(io::write(s, [4, 5]) as size == 2); - assert(bytes::equal(buffer(s), [1, 2, 3, 4, 5])); + assert(io::write(&s, [1, 2, 3]) as size == 3); + assert(bytes::equal(buffer(&s), [1, 2, 3])); + assert(io::write(&s, [4, 5]) as size == 2); + assert(bytes::equal(buffer(&s), [1, 2, 3, 4, 5])); let buf: [2]u8 = [0...]; - assert(io::seek(s, 0, io::whence::SET) as io::off == 0: io::off); - assert(io::read(s, buf[..]) as size == 2 && bytes::equal(buf, [1, 2])); - assert(io::read(s, buf[..]) as size == 2 && bytes::equal(buf, [3, 4])); - assert(io::read(s, buf[..]) as size == 1 && buf[0] == 5); - assert(io::read(s, buf[..]) is io::EOF); - assert(io::write(s, [6, 7, 8]) as size == 3); - assert(bytes::equal(buffer(s), [1, 2, 3, 4, 5, 6, 7, 8])); + assert(io::seek(&s, 0, io::whence::SET) as io::off == 0: io::off); + assert(io::read(&s, buf[..]) as size == 2 && bytes::equal(buf, [1, 2])); + assert(io::read(&s, buf[..]) as size == 2 && bytes::equal(buf, [3, 4])); + assert(io::read(&s, buf[..]) as size == 1 && buf[0] == 5); + assert(io::read(&s, buf[..]) is io::EOF); + assert(io::write(&s, [6, 7, 8]) as size == 3); + assert(bytes::equal(buffer(&s), [1, 2, 3, 4, 5, 6, 7, 8])); reset(s); - assert(len(buffer(s)) == 0); - assert(io::write(s, [1, 2, 3]) as size == 3); - assert(truncate(s) is void); - assert(len(buffer(s)) == 0); + assert(len(buffer(&s)) == 0); + assert(io::write(&s, [1, 2, 3]) as size == 3); + assert(truncate(&s) is void); + assert(len(buffer(&s)) == 0); let sl: []u8 = alloc([1, 2, 3]); let s = dynamic_from(sl, io::mode::WRITE); - assert(io::write(s, [0, 0]) as size == 2); - assert(io::seek(s, 0, io::whence::END) as io::off == 5: io::off); - assert(io::write(s, [4, 5, 6]) as size == 3); - assert(bytes::equal(buffer(s), [0, 0, 1, 2, 3, 4, 5, 6])); - assert(io::read(s, buf[..]) as io::error is errors::unsupported); - io::close(s); + assert(io::write(&s, [0, 0]) as size == 2); + assert(io::seek(&s, 0, io::whence::END) as io::off == 5: io::off); + assert(io::write(&s, [4, 5, 6]) as size == 3); + assert(bytes::equal(buffer(&s), [0, 0, 1, 2, 3, 4, 5, 6])); + assert(io::read(&s, buf[..]) as io::error is errors::unsupported); + io::close(&s); sl = alloc([1, 2]); let s = dynamic_from(sl, io::mode::READ); - assert(io::read(s, buf[..1]) as size == 1 && buf[0] == 1); - assert(io::seek(s, 1, io::whence::CUR) as io::off == 2: io::off); - assert(io::read(s, buf[..]) is io::EOF); - assert(io::write(s, [1, 2]) as io::error is errors::unsupported); - io::close(s); + assert(io::read(&s, buf[..1]) as size == 1 && buf[0] == 1); + assert(io::seek(&s, 1, io::whence::CUR) as io::off == 2: io::off); + assert(io::read(&s, buf[..]) is io::EOF); + assert(io::write(&s, [1, 2]) as io::error is errors::unsupported); + io::close(&s); }; @test fn fixed() void = { let buf: [1024]u8 = [0...]; let stream = fixed(buf, io::mode::WRITE); - defer io::close(stream); + defer io::close(&stream); let n = 0z; - n += io::write(stream, strings::toutf8("hello ")) as size; - n += io::write(stream, strings::toutf8("world")) as size; + n += io::write(&stream, strings::toutf8("hello ")) as size; + n += io::write(&stream, strings::toutf8("world")) as size; assert(bytes::equal(buf[..n], strings::toutf8("hello world"))); - assert(io::seek(stream, 6, io::whence::SET) + assert(io::seek(&stream, 6, io::whence::SET) as io::error is errors::unsupported); let out: [2]u8 = [0...]; let s = fixed([1u8, 2u8], io::mode::READ); - defer io::close(s); - assert(io::read(s, out[..1]) as size == 1 && out[0] == 1); - assert(io::seek(s, 1, io::whence::CUR) as io::off == 2: io::off); - assert(io::read(s, buf[..]) is io::EOF); - assert(io::write(s, [1, 2]) as io::error is errors::unsupported); + defer io::close(&s); + assert(io::read(&s, out[..1]) as size == 1 && out[0] == 1); + assert(io::seek(&s, 1, io::whence::CUR) as io::off == 2: io::off); + assert(io::read(&s, buf[..]) is io::EOF); + assert(io::write(&s, [1, 2]) as io::error is errors::unsupported); }; diff --git a/cmd/hare/subcmds.ha b/cmd/hare/subcmds.ha @@ -539,7 +539,7 @@ fn version(args: []str) void = { fmt::printfln("HAREPATH\t{}", items)!; case let env: str => fmt::printf("HAREPATH\t")!; - bufio::flush(os::stdout)!; + bufio::flush(&os::stdout_bufio)!; fmt::errorf("(from environment)")!; const items = strings::split(env, ":"); defer free(items); diff --git a/cmd/harec/context.ha b/cmd/harec/context.ha @@ -1,10 +1,11 @@ +use bufio; use io; use hare::types; use hare::unit; type context = struct { out: io::handle, - buf: io::handle, + buf: bufio::memstream, store: *types::typestore, unit: *unit::unit, arch: struct { diff --git a/cmd/harec/gen.ha b/cmd/harec/gen.ha @@ -25,7 +25,7 @@ fn gen(out: io::handle, store: *types::typestore, unit: *unit::unit) void = { }, ... }; - defer io::close(ctx.buf); + defer io::close(&ctx.buf); for (let i = 0z; i < len(unit.decls); i += 1) { match (unit.decls[i].decl) { case let func: unit::decl_func => @@ -73,20 +73,20 @@ fn gen_func(ctx: *context, decl: *unit::decl) void = { // on-demand at the start of the function, rather than spread out // through the body. This is more reliable on qbe's ARM backend, and // generates better IL besides. - bufio::reset(ctx.buf); + bufio::reset(&ctx.buf); - fmt::fprintln(ctx.buf, mklabel(ctx, "body"))!; + fmt::fprintln(&ctx.buf, mklabel(ctx, "body"))!; const rval = gen_expr(ctx, body); if (!body.terminates) { if (has_rval) { - emit(ctx.buf, void, qinstr::RET, rval); + emit(&ctx.buf, void, qinstr::RET, rval); } else { - emit(ctx.buf, void, qinstr::RET); + emit(&ctx.buf, void, qinstr::RET); }; }; - io::write(ctx.out, bufio::buffer(ctx.buf))!; + io::write(ctx.out, bufio::buffer(&ctx.buf))!; fmt::fprintln(ctx.out, "}\n")!; }; @@ -103,7 +103,7 @@ fn gen_store(ctx: *context, object: value, value: value) void = { case => void; // Handled below }; const instr = qinstr_store(ctx, object._type); - emit(ctx.buf, void, instr, value, object); + emit(&ctx.buf, void, instr, value, object); }; fn gen_load(ctx: *context, object: value) value = { @@ -121,7 +121,7 @@ fn gen_load(ctx: *context, object: value) value = { const instr = qinstr_load(ctx, object._type); const temp = mktemp(ctx); const ty = qtype_lookup(ctx, object._type, false); - emit(ctx.buf, (ty, temp), instr, object); + emit(&ctx.buf, (ty, temp), instr, object); return value { value = temp, _type = object._type, @@ -196,7 +196,7 @@ fn gen_binding(ctx: *context, expr: *unit::expr) value = { // const lval, instr = allocval(value); const instr = qinstr_alloc(value._type); const lval = mklval(ctx, value); - emit(ctx.buf, lval, instr, value._type.sz); + emit(&ctx.buf, lval, instr, value._type.sz); gen_expr_at(ctx, item.init, value); }; return vvoid; @@ -238,9 +238,9 @@ fn gen_return(ctx: *context, expr: *unit::expr) value = { const rexpr = expr.expr as unit::_return; match (rexpr) { case null => - emit(ctx.buf, void, qinstr::RET); + emit(&ctx.buf, void, qinstr::RET); case let expr: *unit::expr => - emit(ctx.buf, void, qinstr::RET, gen_expr(ctx, expr)); + emit(&ctx.buf, void, qinstr::RET, gen_expr(ctx, expr)); }; return vvoid; }; diff --git a/cmd/harec/main.ha b/cmd/harec/main.ha @@ -69,9 +69,9 @@ export fn main() void = { defer io::close(input); static let buf: [os::BUFSIZ]u8 = [0...]; let bufin = bufio::buffered(input, buf, []); - defer io::close(bufin); + defer io::close(&bufin); - let lexer = lex::init(bufin, cmd.args[i]); + let lexer = lex::init(&bufin, cmd.args[i]); let su = match (parse::subunit(&lexer)) { case let err: parse::error => printerr(err); diff --git a/cmd/haredoc/docstr.ha b/cmd/haredoc/docstr.ha @@ -21,7 +21,7 @@ type docstate = enum { }; type parser = struct { - src: io::handle, + src: bufio::bufstream, state: docstate, }; @@ -34,14 +34,14 @@ fn parsedoc(in: io::handle) parser = { }; fn scandoc(par: *parser) (token | void) = { - const rn = match (bufio::scanrune(par.src)!) { + const rn = match (bufio::scanrune(&par.src)!) { case let rn: rune => yield rn; case io::EOF => return; }; - bufio::unreadrune(par.src, rn); + bufio::unreadrune(&par.src, rn); switch (par.state) { case docstate::TEXT => switch (rn) { @@ -72,18 +72,18 @@ fn scantext(par: *parser) (token | void) = { // TODO: Collapse whitespace const buf = strio::dynamic(); for (true) { - const rn = match (bufio::scanrune(par.src)!) { + const rn = match (bufio::scanrune(&par.src)!) { case io::EOF => break; case let rn: rune => yield rn; }; switch (rn) { case '[' => - bufio::unreadrune(par.src, rn); + bufio::unreadrune(&par.src, rn); break; case '\n' => strio::appendrune(&buf, rn)!; - const rn = match (bufio::scanrune(par.src)!) { + const rn = match (bufio::scanrune(&par.src)!) { case io::EOF => break; case let rn: rune => yield rn; @@ -92,7 +92,7 @@ fn scantext(par: *parser) (token | void) = { par.state = docstate::PARAGRAPH; break; }; - bufio::unreadrune(par.src, rn); + bufio::unreadrune(&par.src, rn); case => strio::appendrune(&buf, rn)!; }; @@ -105,7 +105,7 @@ fn scantext(par: *parser) (token | void) = { }; fn scanref(par: *parser) (token | void) = { - match (bufio::scanrune(par.src)!) { + match (bufio::scanrune(&par.src)!) { case io::EOF => return; case let rn: rune => @@ -113,12 +113,12 @@ fn scanref(par: *parser) (token | void) = { abort(); }; }; - match (bufio::scanrune(par.src)!) { + match (bufio::scanrune(&par.src)!) { case io::EOF => return; case let rn: rune => if (rn != '[') { - bufio::unreadrune(par.src, rn); + bufio::unreadrune(&par.src, rn); return strings::dup("["): text; }; }; @@ -127,11 +127,11 @@ fn scanref(par: *parser) (token | void) = { defer io::close(&buf); // TODO: Handle invalid syntax here for (true) { - match (bufio::scanrune(par.src)!) { + match (bufio::scanrune(&par.src)!) { case let rn: rune => switch (rn) { case ']' => - bufio::scanrune(par.src) as rune; // ] + bufio::scanrune(&par.src) as rune; // ] break; case => strio::appendrune(&buf, rn)!; @@ -146,7 +146,7 @@ fn scanref(par: *parser) (token | void) = { fn scansample(par: *parser) (token | void) = { let nws = 0z; for (true) { - match (bufio::scanrune(par.src)!) { + match (bufio::scanrune(&par.src)!) { case io::EOF => return; case let rn: rune => @@ -156,7 +156,7 @@ fn scansample(par: *parser) (token | void) = { case '\t' => nws += 8; case => - bufio::unreadrune(par.src, rn); + bufio::unreadrune(&par.src, rn); break; }; }; @@ -168,7 +168,7 @@ fn scansample(par: *parser) (token | void) = { let cont = true; let buf = strio::dynamic(); for (cont) { - const rn = match (bufio::scanrune(par.src)!) { + const rn = match (bufio::scanrune(&par.src)!) { case io::EOF => break; case let rn: rune => yield rn; @@ -183,7 +183,7 @@ fn scansample(par: *parser) (token | void) = { // Consume whitespace for (let i = 0z; i < nws) { - match (bufio::scanrune(par.src)!) { + match (bufio::scanrune(&par.src)!) { case io::EOF => break; case let rn: rune => switch (rn) { @@ -195,7 +195,7 @@ fn scansample(par: *parser) (token | void) = { strio::appendrune(&buf, rn)!; i = 0; case => - bufio::unreadrune(par.src, rn); + bufio::unreadrune(&par.src, rn); cont = false; break; }; @@ -216,7 +216,7 @@ fn scansample(par: *parser) (token | void) = { fn scanlist(par: *parser) (token | void) = { let items: list = []; for (true) { - match (bufio::scanrune(par.src)!) { + match (bufio::scanrune(&par.src)!) { case io::EOF => break; case let rn: rune => if (rn != '-') { @@ -224,7 +224,7 @@ fn scanlist(par: *parser) (token | void) = { }; }; // XXX: multi-line list items - append(items, match (bufio::scanline(par.src)!) { + append(items, match (bufio::scanline(&par.src)!) { case io::EOF => break; case let u: []u8 => yield strings::fromutf8(u); diff --git a/cmd/haredoc/html.ha b/cmd/haredoc/html.ha @@ -270,7 +270,7 @@ fn details(ctx: *context, decl: ast::decl) (void | error) = { if (len(decl.docs) != 0) { const buf = strings::toutf8(decl.docs); - markup_html(ctx, bufio::fixed(buf, io::mode::READ))?; + markup_html(ctx, &bufio::fixed(buf, io::mode::READ))?; } else { fmt::fprintln(ctx.out, "</details>")?; }; diff --git a/crypto/argon2/argon2.ha b/crypto/argon2/argon2.ha @@ -37,7 +37,7 @@ type mode = enum { // implementation of argon2 does not process hashes in parallel, though it will // still compute the correct hash if this value is greater than one. // -// 'version' specifies the version of the argon2 function. The implementation +// 'version' specifies the version of the argon2 function. The implementation // currently only supports version 1.3. Use [[VERSION]] here. // // 'secret' and 'data' are optional byte arrays that are applied to the initial @@ -99,7 +99,7 @@ type context = struct { // is recommended, and 4 bytes is the minimum. // // The argon2d mode uses data-dependent memory access and is suitable for -// applications with no threats of side-channel timing attacks. +// applications with no threats of side-channel timing attacks. export fn argon2d( dest: []u8, password: []u8, @@ -338,26 +338,25 @@ fn varhash(dest: []u8, block: []u8) void = { let v: [64]u8 = [0...]; let h = blake2b::blake2b([], 64); let destbuf = bufio::fixed(dest, io::mode::WRITE); - defer io::close(destbuf); hash_leputu32(&h, len(dest): u32); hash::write(&h, block); hash::sum(&h, v[..]); - io::write(destbuf, v[..32])!; + io::write(&destbuf, v[..32])!; for (let i = 1z; i < r; i += 1) { hash::reset(&h); hash::write(&h, v[..]); hash::sum(&h, v[..]); - io::write(destbuf, v[..32])!; + io::write(&destbuf, v[..32])!; }; const remainder = len(dest) - 32 * r; let hend = blake2b::blake2b([], remainder); hash::write(&hend, v[..]); hash::finish(&hend, v[..remainder]); - io::write(destbuf, v[..remainder])!; + io::write(&destbuf, v[..remainder])!; }; fn divceil(dividend: u32, divisor: u32) u32 = { diff --git a/encoding/base64/base64.ha b/encoding/base64/base64.ha @@ -124,9 +124,8 @@ export fn decode_static( in: io::handle, ) (size | errors::invalid | io::error) = { let buf = bufio::fixed(out, io::mode::WRITE); - defer io::close(buf); let dec = decoder(alphabet, in); - match (io::copy(buf, &dec)) { + match (io::copy(&buf, &dec)) { case let err: io::error => match (err) { case errors::invalid => @@ -162,14 +161,13 @@ export fn decodestr_static( export fn decodeslice(alphabet: []u8, in: []u8) ([]u8 | errors::invalid) = { let out = bufio::dynamic(io::mode::WRITE); let in = bufio::fixed(in, io::mode::READ); - defer io::close(in); - let dec = decoder(alphabet, in); - match (io::copy(out, &dec)) { + let dec = decoder(alphabet, &in); + match (io::copy(&out, &dec)) { case io::error => - io::close(out); + io::close(&out); return errors::invalid; case size => - return bufio::finish(out); + return bufio::buffer(&out); }; }; @@ -181,8 +179,7 @@ export fn decodeslice_static( in: []u8, ) (size | errors::invalid) = { let in = bufio::fixed(in, io::mode::READ); - defer io::close(in); // bufio::finish? - match (decode_static(alphabet, out, in)) { + match (decode_static(alphabet, out, &in)) { case let s: size => return s; case errors::invalid => diff --git a/fmt/fmt.ha b/fmt/fmt.ha @@ -37,16 +37,15 @@ export fn errorfln(fmt: str, args: field...) (io::error | size) = // caller must free the return value. export fn asprintf(fmt: str, args: field...) str = { let buf = bufio::dynamic(io::mode::WRITE); - assert(fprintf(buf, fmt, args...) is size); - return strings::fromutf8_unsafe(bufio::finish(buf)); + assert(fprintf(&buf, fmt, args...) is size); + return strings::fromutf8_unsafe(bufio::buffer(&buf)); }; // Formats text for printing and writes it into a caller supplied buffer. The // returned string is borrowed from this buffer. export fn bsprintf(buf: []u8, fmt: str, args: field...) str = { let sink = bufio::fixed(buf, io::mode::WRITE); - defer io::close(sink); - let l = fprintf(sink, fmt, args...) as size; + let l = fprintf(&sink, fmt, args...) as size; return strings::fromutf8_unsafe(buf[..l]); }; @@ -92,8 +91,8 @@ export fn errorln(args: formattable...) (io::error | size) = // the return value. export fn asprint(args: formattable...) str = { let buf = bufio::dynamic(io::mode::WRITE); - assert(fprint(buf, args...) is size); - return strings::fromutf8_unsafe(bufio::finish(buf)); + assert(fprint(&buf, args...) is size); + return strings::fromutf8_unsafe(bufio::buffer(&buf)); }; // Formats values for printing using the default format modifiers and writes @@ -101,8 +100,7 @@ export fn asprint(args: formattable...) str = { // is borrowed from this buffer. export fn bsprint(buf: []u8, args: formattable...) str = { let sink = bufio::fixed(buf, io::mode::WRITE); - defer io::close(sink); - assert(fprint(sink, args...) is size); + assert(fprint(&sink, args...) is size); return strings::fromutf8_unsafe(buf); }; diff --git a/format/xml/parser.ha b/format/xml/parser.ha @@ -22,15 +22,16 @@ export fn parse(in: io::handle) (*parser | error) = { // stack is so that we have a consistent address for the bufio buffer. // This is kind of lame, maybe we can avoid that. let par = alloc(parser { - in = in, close = false, namebuf = strio::dynamic(), entbuf = strio::dynamic(), textbuf = strio::dynamic(), ... }); - if (!bufio::isbuffered(in)) { - par.in = bufio::buffered(par.in, par.buf[..], []); + if (bufio::isbuffered(in)) { + par.in = in as *io::stream: *bufio::bufstream; + } else { + par.in = alloc(bufio::buffered(par.in, par.buf[..], [])); par.close = true; }; prolog(par)?; @@ -41,7 +42,7 @@ export fn parse(in: io::handle) (*parser | error) = { // underlying I/O handle. export fn parser_free(par: *parser) void = { if (par.close) { - io::close(par.in); + free(par.in); }; io::close(&par.namebuf); io::close(&par.entbuf); diff --git a/format/xml/types.ha b/format/xml/types.ha @@ -1,10 +1,11 @@ +use bufio; use encoding::utf8; use io; use os; use strio; export type parser = struct { - in: io::handle, + in: *bufio::bufstream, buf: [os::BUFSIZ]u8, close: bool, state: state, diff --git a/fs/mem/stream.ha b/fs/mem/stream.ha @@ -6,7 +6,7 @@ use types; type stream = struct { io::stream, - source: *io::stream, + source: bufio::memstream, inode: *inode, appnd: bool, }; @@ -40,43 +40,43 @@ fn stream_open( ino.opencount = types::SIZE_MAX; s.source = bufio::dynamic_from(f, mode); if (!appnd) { - bufio::truncate(s.source)?; + bufio::truncate(&s.source)?; }; }; - io::seek(s.source, 0, io::whence::SET)?; + io::seek(&s.source, 0, io::whence::SET)?; return s; }; fn read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = { - return io::read((s: *stream).source, buf); + return io::read(&(s: *stream).source, buf); }; fn write(s: *io::stream, buf: const []u8) (size | io::error) = { let s = s: *stream; if (s.appnd) { - io::seek(s.source, 0, io::whence::END)?; + io::seek(&s.source, 0, io::whence::END)?; }; - let sz = io::write(s.source, buf)?; - s.inode.data = bufio::buffer(s.source); + let sz = io::write(&s.source, buf)?; + s.inode.data = bufio::buffer(&s.source); return sz; }; fn seek(s: *io::stream, off: io::off, w: io::whence) (io::off | io::error) = { - return io::seek((s: *stream).source, off, w); + return io::seek(&(s: *stream).source, off, w); }; fn stream_close(s: *io::stream) void = { let s = s: *stream; defer free(s); if (s.writer == null) { - io::close(s.source); + io::close(&s.source); s.inode.opencount -= 1; if (s.inode.opencount > 0) { return; }; } else { s.inode.opencount = 0; - bufio::finish(s.source); + io::close(&s.source); }; if (s.inode.parent == null) { diff --git a/hare/module/manifest.ha b/hare/module/manifest.ha @@ -67,7 +67,7 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = { let buf: [4096]u8 = [0...]; let file = bufio::buffered(file, buf, []); for (true) { - let line = match (bufio::scanline(file)?) { + let line = match (bufio::scanline(&file)?) { case io::EOF => break; case let line: []u8 => diff --git a/hare/parse/ident.ha b/hare/parse/ident.ha @@ -42,7 +42,6 @@ export fn ident(lexer: *lex::lexer) (ast::ident | error) = { // caller needn't provide a lexer instance. export fn identstr(in: str) (ast::ident | error) = { const buf = bufio::fixed(strings::toutf8(in), io::mode::READ); - defer io::close(buf); - const lexer = lex::init(buf, "<string>"); + const lexer = lex::init(&buf, "<string>"); return ident(&lexer); }; diff --git a/os/+linux/stdfd.ha b/os/+linux/stdfd.ha @@ -1,12 +1,12 @@ use bufio; use io; -let static_stdin_bufio: bufio::bufstream = bufio::bufstream { +export let stdin_bufio: bufio::bufstream = bufio::bufstream { source = 0, ... }; -let static_stdout_bufio: bufio::bufstream = bufio::bufstream { +export let stdout_bufio: bufio::bufstream = bufio::bufstream { source = 1, ... }; @@ -31,10 +31,12 @@ export def BUFSIZ: size = 4096; // 4 KiB @init fn init_stdfd() void = { static let stdinbuf: [BUFSIZ]u8 = [0...]; - stdin = bufio::static_buffered(0, stdinbuf, [], &static_stdin_bufio); + stdin_bufio = bufio::buffered(0, stdinbuf, []); + stdin = &stdin_bufio; static let stdoutbuf: [BUFSIZ]u8 = [0...]; - stdout = bufio::static_buffered(1, [], stdoutbuf, &static_stdout_bufio); + stdout_bufio = bufio::buffered(0, [], stdoutbuf); + stdout = &stdout_bufio; }; @fini fn fini_stdfd() void = { diff --git a/path/join.ha b/path/join.ha @@ -18,15 +18,15 @@ export fn join(paths: str...) str = { l -= 1; }; for (let q = 0z; q < l) { - let w = io::write(sink, buf[q..l]) as size; + let w = io::write(&sink, buf[q..l]) as size; q += w; }; if (i + 1 < len(paths)) { - assert(io::write(sink, [PATHSEP]) as size == 1); + assert(io::write(&sink, [PATHSEP]) as size == 1); }; }; - return strings::fromutf8_unsafe(bufio::finish(sink)); + return strings::fromutf8_unsafe(bufio::buffer(&sink)); }; @test fn join() void = { diff --git a/unix/hosts/lookup.ha b/unix/hosts/lookup.ha @@ -29,9 +29,7 @@ export fn lookup(name: str) []ip::addr = { }; const scanner = bufio::fixed(line, io::mode::READ); - defer io::close(scanner); - - const tok = match (bufio::scantok(scanner, ' ', '\t')!) { + const tok = match (bufio::scantok(&scanner, ' ', '\t')!) { case io::EOF => break; case let tok: []u8 => @@ -41,7 +39,7 @@ export fn lookup(name: str) []ip::addr = { const addr = ip::parse(strings::fromutf8(tok))!; for (true) { - const tok = match (bufio::scantok(scanner, ' ', '\t')!) { + const tok = match (bufio::scantok(&scanner, ' ', '\t')!) { case io::EOF => break; case let tok: []u8 => diff --git a/unix/resolvconf/load.ha b/unix/resolvconf/load.ha @@ -38,9 +38,7 @@ export fn load() []ip::addr = { }; const scanner = bufio::fixed(line, io::mode::READ); - defer io::close(scanner); - - const tok = match (bufio::scantok(scanner, ' ', '\t')!) { + const tok = match (bufio::scantok(&scanner, ' ', '\t')!) { case io::EOF => break; case let tok: []u8 => @@ -51,7 +49,7 @@ export fn load() []ip::addr = { continue; }; - const tok = match (bufio::scantok(scanner, ' ')!) { + const tok = match (bufio::scantok(&scanner, ' ')!) { case io::EOF => break; case let tok: []u8 => diff --git a/uuid/uuid.ha b/uuid/uuid.ha @@ -145,8 +145,7 @@ export fn decode(in: io::handle) (uuid | invalid | io::error) = { // Decodes a UUID from a string. export fn decodestr(in: str) (uuid | invalid) = { let buf = bufio::fixed(strings::toutf8(in), io::mode::READ); - defer io::close(buf); - match (decode(buf)) { + match (decode(&buf)) { case let err: io::error => abort(); case invalid =>