commit 0df9af142931b1150589e6f8c52e73b2c8092d4c
parent 1809e28695c9ed52c59b9571dafea3f794dbb4dc
Author: Drew DeVault <sir@cmpwn.com>
Date: Sat, 16 Oct 2021 10:58:05 +0200
all: introduce io::handle and refactor usage
The goal of this round of refactoring is to simplify the API a bit and
reduce the ABI footprint of io::file (and io::handle).
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
75 files changed, 626 insertions(+), 625 deletions(-)
diff --git a/bufio/buffered.ha b/bufio/buffered.ha
@@ -6,7 +6,7 @@ use strings;
export type bufstream = struct {
stream: io::stream,
- source: *io::stream,
+ source: io::handle,
rbuffer: []u8,
wbuffer: []u8,
ravail: size,
@@ -31,10 +31,10 @@ export type bufstream = struct {
// let wbuf: [os::BUFSIZ]u8 = [0...];
// let buffered = bufio::buffered(source, rbuf, wbuf);
export fn buffered(
- src: *io::stream,
+ src: io::handle,
rbuf: []u8,
wbuf: []u8,
-) *io::stream = {
+) io::handle = {
let s = alloc(bufstream { ... });
let st = static_buffered(src, rbuf, wbuf, s);
st.closer = &buffered_close;
@@ -42,7 +42,7 @@ export fn buffered(
};
export fn static_buffered(
- src: *io::stream,
+ src: io::handle,
rbuf: []u8,
wbuf: []u8,
s: *bufstream,
@@ -50,9 +50,7 @@ export fn static_buffered(
static let flush_default = ['\n': u32: u8];
*s = bufstream {
stream = io::stream {
- name = src.name,
closer = &buffered_close_static,
- unwrap = &buffered_unwrap,
...
},
source = src,
@@ -74,11 +72,14 @@ export fn static_buffered(
return &s.stream;
};
+fn getstream(in: io::handle) *bufstream = {
+ assert(isbuffered(in), "Attempted to use bufio on unbuffered stream");
+ return in as *io::stream: *bufstream;
+};
+
// Flushes pending writes to the underlying stream.
-export fn flush(s: *io::stream) (io::error | void) = {
- assert(s.writer == &buffered_write,
- "bufio::flushed used on non-buffered stream");
- let s = s: *bufstream;
+export fn flush(s: io::handle) (io::error | void) = {
+ let s = getstream(s);
if (s.wavail == 0) {
return;
};
@@ -89,48 +90,36 @@ export fn flush(s: *io::stream) (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::stream, b: []u8) void = {
- assert(s.writer == &buffered_write,
- "bufio: setflush used on non-buffered stream");
- let s = s: *bufstream;
+export fn setflush(s: io::handle, b: []u8) void = {
+ let s = getstream(s);
s.flush = b;
};
-// Returns true if this is a buffered stream.
-export fn isbuffered(s: *io::stream) bool = {
- return s.reader == &buffered_read || s.writer == &buffered_write;
-};
-
-// Returns true if this stream or any underlying streams are buffered.
-export fn any_isbuffered(s: *io::stream) bool = {
- for (!isbuffered(s)) {
- s = match (io::source(s)) {
- case errors::unsupported =>
- return false;
- case s: *io::stream =>
- yield s;
- };
- };
- return true;
-};
-
// 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::stream, buf: []u8) void = {
- assert(isbuffered(s), "bufio: unread used on non-buffered stream");
- let s = s: *bufstream;
+export fn unread(s: io::handle, buf: []u8) void = {
+ let s = getstream(s);
append(s.unread, buf...);
};
// Unreads a rune; see [[unread]].
-export fn unreadrune(s: *io::stream, rn: rune) void = {
- assert(isbuffered(s), "bufio: unread used on non-buffered stream");
- let s = s: *bufstream;
+export fn unreadrune(s: io::handle, rn: rune) void = {
+ let s = getstream(s);
const buf = utf8::encoderune(rn);
insert(s.unread[0], buf...);
};
+// Returns true if an [[io::handle]] is a [[buffered]] stream.
+export fn isbuffered(in: io::handle) bool = {
+ match (in) {
+ case io::file =>
+ return false;
+ case st: *io::stream =>
+ return st.reader == &buffered_read || st.writer == &buffered_write;
+ };
+};
+
fn buffered_close(s: *io::stream) void = {
assert(s.closer == &buffered_close);
if (s.writer != null) {
@@ -146,12 +135,6 @@ fn buffered_close_static(s: *io::stream) void = {
};
};
-fn buffered_unwrap(s: *io::stream) *io::stream = {
- assert(s.unwrap == &buffered_unwrap);
- let s = s: *bufstream;
- return s.source;
-};
-
fn buffered_read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = {
assert(s.reader == &buffered_read);
let s = s: *bufstream;
diff --git a/bufio/memstream.ha b/bufio/memstream.ha
@@ -15,7 +15,6 @@ type memstream = struct {
export fn fixed(in: []u8, mode: io::mode) *io::stream = {
let s = alloc(memstream {
stream = io::stream {
- name = "<bufio::fixed>",
closer = &fixed_close,
...
},
@@ -65,7 +64,6 @@ export fn dynamic(mode: io::mode) *io::stream = dynamic_from([], mode);
export fn dynamic_from(in: []u8, mode: io::mode) *io::stream = {
let s = alloc(memstream {
stream = io::stream {
- name = "<bufio::dynamic>",
closer = &dynamic_close,
seeker = &seek,
...
@@ -82,62 +80,59 @@ export fn dynamic_from(in: []u8, mode: io::mode) *io::stream = {
return s;
};
-fn dynamic_write(s: *io::stream, buf: const []u8) (size | io::error) = {
- let s = s: *memstream;
- insert(s.buf[s.pos], buf...);
- s.pos += len(buf);
- return len(buf);
-};
-
-fn dynamic_close(s: *io::stream) void = {
- const s = s: *memstream;
- free(s.buf);
- free(s);
+fn getmemstream(in: io::handle) *memstream = {
+ match (in) {
+ case io::file =>
+ abort("Invalid use of bufio with unbuffered file");
+ case 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(s: *io::stream) []u8 = {
- if (s.closer != &dynamic_close) {
- abort("bufio::finish called on non-bufio::dynamic stream");
- };
- let s = s: *memstream;
+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(s: *io::stream) []u8 = {
- if (s.closer != &dynamic_close) {
- abort("bufio::buffer called on non-bufio::dynamic stream");
- };
- let s = s: *memstream;
+export fn buffer(in: io::handle) []u8 = {
+ const s = getmemstream(in);
return s.buf;
};
// Resets the dynamic buffer's length to zero, but keeps the allocated memory
// around for future writes.
-export fn reset(s: *io::stream) void = {
- if (s.writer != &dynamic_write) {
- abort("bufio::reset called on non-bufio::dynamic stream");
- };
- const s = s: *memstream;
+export fn reset(in: io::handle) void = {
+ const s = getmemstream(in);
s.pos = 0;
s.buf = s.buf[..0];
};
// Truncates the dynamic buffer, freeing memory associated with it and setting
// its length to zero.
-export fn truncate(s: *io::stream) (void | errors::unsupported) = {
- if (s.writer != &dynamic_write) {
- return errors::unsupported;
- };
- let s = s: *memstream;
+export fn truncate(in: io::handle) (void | errors::unsupported) = {
+ let s = getmemstream(in);
s.pos = 0;
delete(s.buf[..]);
};
+fn dynamic_write(s: *io::stream, buf: const []u8) (size | io::error) = {
+ let s = s: *memstream;
+ insert(s.buf[s.pos], buf...);
+ s.pos += len(buf);
+ return len(buf);
+};
+
+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) = {
let s = s: *memstream;
diff --git a/bufio/scanner.ha b/bufio/scanner.ha
@@ -4,11 +4,11 @@ use io;
use strings;
use types;
-// Reads a single byte from the stream.
-export fn scanbyte(stream: *io::stream) (u8 | io::EOF | io::error) = {
+// Reads a single byte from an [[io::handle]].
+export fn scanbyte(file: io::handle) (u8 | io::EOF | io::error) = {
let buf: [1]u8 = [0...];
- match (io::read(stream, buf)?) {
+ match (io::read(file, buf)?) {
case read: size =>
if (read > 0) {
return buf[0];
@@ -22,11 +22,11 @@ export fn scanbyte(stream: *io::stream) (u8 | io::EOF | io::error) = {
// Reads a slice of bytes until the delimiter. Delimiter is not included. The
// return value must be freed by the caller.
-export fn scantok(stream: *io::stream, delim: u8...) ([]u8 | io::EOF | io::error) = {
+export fn scantok(file: io::handle, delim: u8...) ([]u8 | io::EOF | io::error) = {
let buf: []u8 = [];
for (true) {
- match (scanbyte(stream)?) {
+ match (scanbyte(file)?) {
case res: u8 =>
if (bytes::contains(delim, res)) {
break;
@@ -45,13 +45,15 @@ export fn scantok(stream: *io::stream, delim: u8...) ([]u8 | io::EOF | io::error
// Reads a slice of bytes until a newline character (\n, 0x10). Newline itself
// is not included. The return value must be freed by the caller.
-export fn scanline(stream: *io::stream) ([]u8 | io::EOF | io::error) =
- scantok(stream, '\n': u32: u8);
+export fn scanline(file: io::handle) ([]u8 | io::EOF | io::error) =
+ scantok(file, '\n': u32: u8);
// Reads a rune from a UTF-8 stream.
-export fn scanrune(stream: *io::stream) (rune | utf8::invalid | io::EOF | io::error) = {
+export fn scanrune(
+ file: io::handle,
+) (rune | utf8::invalid | io::EOF | io::error) = {
let b: [4]u8 = [0...];
- match (io::read(stream, b[..1])?) {
+ match (io::read(file, b[..1])?) {
case n: size =>
assert(n == 1);
case io::EOF =>
@@ -67,7 +69,7 @@ export fn scanrune(stream: *io::stream) (rune | utf8::invalid | io::EOF | io::er
return b[0]: u32: rune;
};
- match (io::read(stream, b[1..sz])?) {
+ match (io::read(file, b[1..sz])?) {
case n: size =>
assert(n == sz - 1);
case io::EOF =>
diff --git a/cmd/harec/context.ha b/cmd/harec/context.ha
@@ -3,8 +3,8 @@ use hare::types;
use hare::unit;
type context = struct {
- out: *io::stream,
- buf: *io::stream,
+ out: io::handle,
+ buf: io::handle,
store: *types::typestore,
unit: *unit::unit,
arch: struct {
diff --git a/cmd/harec/errors.ha b/cmd/harec/errors.ha
@@ -19,17 +19,17 @@ fn printerr(err: parse::error) void = {
fn printerr_syntax(err: lex::syntax) void = {
let location = err.0, details = err.1;
let file = os::open(location.path)!;
- defer io::close(&file);
+ defer io::close(file);
let line = 1u;
for (line < location.line) {
- let r = bufio::scanrune(&file) as rune;
+ let r = bufio::scanrune(file) as rune;
if (r == '\n') {
line += 1u;
};
};
- let line = bufio::scanline(&file) as []u8;
+ let line = bufio::scanline(file) as []u8;
defer free(line);
let line = strings::fromutf8(line);
fmt::errorfln("{}:{},{}: Syntax error: {}",
diff --git a/cmd/harec/gen.ha b/cmd/harec/gen.ha
@@ -11,7 +11,7 @@ use io;
use os;
use strings;
-fn gen(out: *io::stream, store: *types::typestore, unit: *unit::unit) void = {
+fn gen(out: io::handle, store: *types::typestore, unit: *unit::unit) void = {
// TODO: context_init
let ctx = context {
out = out,
diff --git a/cmd/harec/main.ha b/cmd/harec/main.ha
@@ -26,7 +26,6 @@ export fn main() void = {
defer getopt::finish(&cmd);
let out = os::stdout;
-
for (let i = 0z; i < len(cmd.opts); i += 1) {
let opt = cmd.opts[i];
switch (opt.0) {
@@ -36,7 +35,7 @@ export fn main() void = {
case 'o' =>
out = match (os::create(opt.1, 0o644)) {
case f: io::file =>
- yield &f;
+ yield f;
case e: fs::error =>
fmt::fatal(fs::strerror(e));
};
@@ -67,9 +66,9 @@ export fn main() void = {
fmt::fatal("Error opening {}: {}",
cmd.args[i], fs::strerror(err));
};
- defer io::close(&input);
+ defer io::close(input);
static let buf: [os::BUFSIZ]u8 = [0...];
- let bufin = bufio::buffered(&input, buf, []);
+ let bufin = bufio::buffered(input, buf, []);
defer io::close(bufin);
let lexer = lex::init(bufin, cmd.args[i]);
diff --git a/cmd/harec/qbe.ha b/cmd/harec/qbe.ha
@@ -22,7 +22,7 @@ const vvoid: value = value {
};
fn emit(
- to: *io::stream,
+ to: io::handle,
out: (qtypeval | void),
instr: qinstr,
args: (value | qval | qtype)...
@@ -230,7 +230,7 @@ fn emit(
fmt::fprintln(to)!;
};
-fn qval_emit(to: *io::stream, qv: qval) void = {
+fn qval_emit(to: io::handle, qv: qval) void = {
match (qv) {
case g: global =>
fmt::fprintf(to, " ${}", g)!;
diff --git a/cmd/haredoc/docstr.ha b/cmd/haredoc/docstr.ha
@@ -21,11 +21,11 @@ type docstate = enum {
};
type parser = struct {
- src: *io::stream,
+ src: io::handle,
state: docstate,
};
-fn parsedoc(in: *io::stream) parser = {
+fn parsedoc(in: io::handle) parser = {
static let buf: [4096]u8 = [0...];
return parser {
src = bufio::buffered(in, buf[..], []),
diff --git a/cmd/haredoc/hare.ha b/cmd/haredoc/hare.ha
@@ -17,7 +17,7 @@ fn emit_hare(ctx: *context) (void | error) = {
case readme: io::file =>
first = false;
for (true) {
- match (bufio::scanline(&readme)?) {
+ match (bufio::scanline(readme)?) {
case io::EOF => break;
case b: []u8 =>
fmt::printfln("// {}", strings::fromutf8(b))?;
@@ -87,7 +87,7 @@ fn details_hare(ctx: *context, decl: ast::decl) (void | error) = {
};
// Forked from [[hare::unparse]]
-fn unparse_hare(out: *io::stream, d: ast::decl) (size | io::error) = {
+fn unparse_hare(out: io::handle, d: ast::decl) (size | io::error) = {
let n = 0z;
match (d.decl) {
case g: []ast::decl_global =>
@@ -154,7 +154,7 @@ fn unparse_hare(out: *io::stream, d: ast::decl) (size | io::error) = {
};
fn prototype_hare(
- out: *io::stream,
+ out: io::handle,
indent: size,
t: ast::func_type,
) (size | io::error) = {
diff --git a/cmd/haredoc/html.ha b/cmd/haredoc/html.ha
@@ -12,9 +12,9 @@ use path;
use strings;
use strio;
-// Prints a string to an output stream, escaping any of HTML's reserved
+// Prints a string to an output handle, escaping any of HTML's reserved
// characters.
-fn html_escape(out: *io::stream, in: str) (size | io::error) = {
+fn html_escape(out: io::handle, in: str) (size | io::error) = {
let z = 0z;
let iter = strings::iter(in);
for (true) {
@@ -85,7 +85,7 @@ fn emit_html(ctx: *context) (void | error) = {
case void => void;
case f: io::file =>
fmt::println("<div class='readme'>")?;
- markup_html(ctx, &f)?;
+ markup_html(ctx, f)?;
fmt::println("</div>")?;
};
@@ -305,7 +305,7 @@ fn htmlref(ctx: *context, ref: ast::ident) (void | io::error) = {
free(ident);
};
-fn markup_html(ctx: *context, in: *io::stream) (void | io::error) = {
+fn markup_html(ctx: *context, in: io::handle) (void | io::error) = {
let parser = parsedoc(in);
for (true) {
const tok = match (scandoc(&parser)) {
@@ -352,7 +352,7 @@ fn markup_html(ctx: *context, in: *io::stream) (void | io::error) = {
};
// Forked from [[hare::unparse]]
-fn unparse_html(out: *io::stream, d: ast::decl) (size | io::error) = {
+fn unparse_html(out: io::handle, d: ast::decl) (size | io::error) = {
let n = 0z;
match (d.decl) {
case c: []ast::decl_const =>
@@ -462,7 +462,7 @@ fn builtin_type(b: ast::builtin_type) str = {
};
// Forked from [[hare::unparse]].
-fn newline(out: *io::stream, indent: size) (size | io::error) = {
+fn newline(out: io::handle, indent: size) (size | io::error) = {
let n = 0z;
n += fmt::fprint(out, "\n")?;
for (let i = 0z; i < indent; i += 1) {
@@ -472,7 +472,7 @@ fn newline(out: *io::stream, indent: size) (size | io::error) = {
};
fn enum_html(
- out: *io::stream,
+ out: io::handle,
indent: size,
t: ast::enum_type
) (size | io::error) = {
@@ -505,7 +505,7 @@ fn enum_html(
};
fn struct_union_html(
- out: *io::stream,
+ out: io::handle,
indent: size,
t: ast::_type,
brief: bool,
@@ -553,7 +553,7 @@ fn struct_union_html(
};
fn type_html(
- out: *io::stream,
+ out: io::handle,
indent: size,
_type: ast::_type,
brief: bool,
@@ -668,7 +668,7 @@ fn type_html(
};
fn prototype_html(
- out: *io::stream,
+ out: io::handle,
indent: size,
t: ast::func_type,
brief: bool,
diff --git a/cmd/haredoc/main.ha b/cmd/haredoc/main.ha
@@ -32,7 +32,11 @@ type context = struct {
};
export fn main() void = {
- let fmt = if (tty::isatty(os::stdout)) format::TTY else format::HARE;
+ let fmt =
+ if (tty::isatty(os::stdout_file))
+ format::TTY
+ else
+ format::HARE;
let template = true;
let show_undocumented = false;
const help: [_]getopt::help = [
@@ -139,7 +143,7 @@ export fn main() void = {
defer match (readme) {
case void => void;
case f: io::file =>
- io::close(&f);
+ io::close(f);
};
if (decl != "") {
@@ -212,8 +216,8 @@ fn scan(path: str) (ast::subunit | error) = {
case err: fs::error =>
fmt::fatal("Error reading {}: {}", path, fs::strerror(err));
};
- defer io::close(&input);
- const lexer = lex::init(&input, path, lex::flags::COMMENTS);
+ defer io::close(input);
+ const lexer = lex::init(input, path, lex::flags::COMMENTS);
return parse::subunit(&lexer)?;
};
diff --git a/cmd/haredoc/tty.ha b/cmd/haredoc/tty.ha
@@ -17,7 +17,7 @@ fn emit_tty(ctx: *context) (void | error) = {
match (ctx.readme) {
case readme: io::file =>
- for (true) match (bufio::scanline(&readme)?) {
+ for (true) match (bufio::scanline(readme)?) {
case io::EOF => break;
case b: []u8 =>
firstline = false;
@@ -88,7 +88,7 @@ fn details_tty(ctx: *context, decl: ast::decl) (void | error) = {
};
// Forked from [[hare::unparse]]
-fn unparse_tty(out: *io::stream, d: ast::decl) (size | io::error) = {
+fn unparse_tty(out: io::handle, d: ast::decl) (size | io::error) = {
let n = 0z;
match (d.decl) {
case g: []ast::decl_global =>
@@ -163,7 +163,7 @@ fn unparse_tty(out: *io::stream, d: ast::decl) (size | io::error) = {
};
fn prototype_tty(
- out: *io::stream,
+ out: io::handle,
indent: size,
t: ast::func_type,
) (size | io::error) = {
@@ -194,7 +194,7 @@ fn prototype_tty(
// Forked from [[hare::unparse]]
fn struct_union_type_tty(
- out: *io::stream,
+ out: io::handle,
indent: size,
t: ast::_type,
) (size | io::error) = {
@@ -243,7 +243,7 @@ fn struct_union_type_tty(
// Forked from [[hare::unparse]]
fn type_tty(
- out: *io::stream,
+ out: io::handle,
indent: size,
t: ast::_type,
) (size | io::error) = {
diff --git a/compress/flate/inflate.ha b/compress/flate/inflate.ha
@@ -36,7 +36,7 @@ type inflate_err = enum u8 {
export type decompressor = struct {
io::stream,
- in: *io::stream,
+ in: io::handle,
bitbuf: u32,
cnt: u32,
final: bool,
@@ -77,10 +77,10 @@ export type decompressor = struct {
};
// Creates a stream which decompresses Deflate (RFC 1951) data.
-export fn inflate(s: *io::stream) decompressor = decompressor {
+export fn inflate(src: io::handle) decompressor = decompressor {
reader = &read,
closer = &close,
- in = s,
+ in = src,
bitbuf = 0,
cnt = 0,
final = false,
diff --git a/compress/zlib/.testdata/gen.ha b/compress/zlib/.testdata/gen.ha
@@ -5,16 +5,16 @@ use io;
use os;
fn write(name: str, buf: []u8) void = {
- fmt::printfln("const {}: []u8 = [", name);
+ fmt::printfln("const {}: []u8 = [", name)!;
for (let i = 0z; i < len(buf); i += 1) {
- fmt::print("\t");
+ fmt::print("\t")!;
for (let j = 0z; j < 11 && i < len(buf) - 1; j += 1) {
- fmt::printf("0x{:02X}, ", buf[i]);
+ fmt::printf("0x{:02X}, ", buf[i])!;
i += 1;
};
- fmt::printfln("0x{:02X},", buf[i]);
+ fmt::printfln("0x{:02X},", buf[i])!;
};
- fmt::println("];\n");
+ fmt::println("];\n")!;
};
export fn main() void = {
@@ -25,38 +25,26 @@ export fn main() void = {
];
for (let i = 0z; i < len(vectors); i += 1) {
- let in = match (os::open(vectors[i].0, fs::flags::RDONLY)) {
- s: *io::stream => s,
- e: fs::error => fmt::fatal(fs::strerror(e)),
- };
+ const in = os::open(vectors[i].0)!;
defer io::close(in);
- let ins = bufio::dynamic(io::mode::WRITE);
- match (io::copy(ins, in)) {
- size => void,
- e: io::error => fmt::fatal(io::strerror(e)),
- };
- let inb = bufio::finish(ins);
+ const ins = bufio::dynamic(io::mode::WRITE);
+ io::copy(ins, in)!;
+ const inb = bufio::finish(ins);
defer free(inb);
write(vectors[i].0, inb);
- let out = match (os::open(vectors[i].1, fs::flags::RDONLY)) {
- s: *io::stream => s,
- e: fs::error => fmt::fatal(fs::strerror(e)),
- };
+ const out = os::open(vectors[i].1)!;
defer io::close(out);
- let outs = bufio::dynamic(io::mode::WRITE);
- match (io::copy(outs, out)) {
- size => void,
- e: io::error => fmt::fatal(io::strerror(e)),
- };
- let outb = bufio::finish(ins);
+ const outs = bufio::dynamic(io::mode::WRITE);
+ io::copy(outs, out)!;
+ const outb = bufio::finish(ins);
defer free(outb);
write(vectors[i].1, outb);
};
- fmt::printfln("const vectors: [_](*[]u8, *[]u8) = [");
+ fmt::printfln("const vectors: [_](*[]u8, *[]u8) = [")!;
for (let i = 0z; i < len(vectors); i += 1) {
- fmt::printfln("\t(&{}, &{}),", vectors[i].0, vectors[i].1);
+ fmt::printfln("\t(&{}, &{}),", vectors[i].0, vectors[i].1)!;
};
- fmt::println("];");
+ fmt::println("];")!;
};
diff --git a/compress/zlib/reader.ha b/compress/zlib/reader.ha
@@ -17,7 +17,7 @@ def FLEVEL: u8 = 0b11000000;
export type reader = struct {
io::stream,
- source: *io::stream,
+ source: io::handle,
flate: flate::decompressor,
hash: adler32::state,
};
@@ -85,10 +85,10 @@ fn close(s: *io::stream) void = {
};
// Creates a stream which decompresses zlib (RFC 1950) data.
-export fn decompress(s: *io::stream) (reader | io::error) = {
+export fn decompress(src: io::handle) (reader | io::error) = {
let buf: [2]u8 = [0...];
for (let n = 0z; n < len(buf)) {
- match (io::read(s, buf[n..])?) {
+ match (io::read(src, buf[n..])?) {
case io::EOF =>
return wraperror(decompress_err::EOF);
case z: size =>
@@ -115,8 +115,8 @@ export fn decompress(s: *io::stream) (reader | io::error) = {
return reader {
reader = &read,
closer = &close,
- source = s,
- flate = flate::inflate(s),
+ source = src,
+ flate = flate::inflate(src),
hash = adler32::adler32(),
...
};
diff --git a/crypto/random/random.ha b/crypto/random/random.ha
@@ -2,12 +2,11 @@ use io;
use rt;
let _stream: io::stream = io::stream {
- name = "<random>",
reader = &rand_reader,
...
};
-// An [[io::stream]] which returns cryptographically random data on reads. Be
+// An [[io::handle]] which returns cryptographically random data on reads. Be
// aware, it may return less than you asked for!
export let stream: *io::stream = &_stream;
diff --git a/encoding/base64/README b/encoding/base64/README
@@ -1,9 +1,9 @@
Implementation of the base 64 encoding as defined by RFC 4648.
There are various functions available for decoding and encoding. The decode
-family accepts an [[io::stream]] as input, while the decodeslice and decodestr
+family accepts an [[io::handle]] as input, while the decodeslice and decodestr
family of functions accept slices and strings as input, respectively.
-[[decode]] accepts an [[io::stream]] for the output, and [[decodeslice]] and
+[[decode]] accepts an [[io::handle]] for the output, and [[decodeslice]] and
[[decodestr]] dynamically allocate a slice or string to write the output to, and
return it to the caller (who is then responsible for freeing it). The _static
family of functions, such as [[decode_static]], accept a caller-allocated slice
diff --git a/encoding/base64/base64.ha b/encoding/base64/base64.ha
@@ -38,10 +38,10 @@ export def PADDING: u8 = '=': u32: u8;
export type invalid = !size;
// Encodes a byte slice using a base 64 encoding alphabet, with padding, and
-// writes it to an [[io::stream]]. The number of bytes written is returned.
+// writes it to an [[io::handle]]. The number of bytes written is returned.
export fn encode(
alphabet: []u8,
- sink: *io::stream,
+ sink: io::handle,
b: []u8
) (size | io::error) = {
let z = 0z;
@@ -101,11 +101,11 @@ export fn encodestr(alphabet: []u8, b: []u8) str = {
};
// Decodes base 64-encoded data in the given base 64 alphabet, with padding,
-// from an [[io::stream]]. The number of bytes written is returned.
+// from an [[io::handle]]. The number of bytes written is returned.
export fn decode(
alphabet: []u8,
- in: *io::stream,
- out: *io::stream,
+ in: io::handle,
+ out: io::handle,
) (size | invalid | io::error) = {
const INVALID_OR_PAD = 255u8;
let decoder: [256]u8 = [INVALID_OR_PAD...];
@@ -181,11 +181,11 @@ export fn decode(
};
// Decodes base 64-encoded data in the given base 64 alphabet, with padding,
-// from an [[io::stream]]. The number of bytes written is returned.
+// from an [[io::handle]]. The number of bytes written is returned.
export fn decode_static(
alphabet: []u8,
out: []u8,
- in: *io::stream,
+ in: io::handle,
) (size | invalid) = {
let buf = bufio::fixed(out, io::mode::WRITE);
defer io::close(buf);
diff --git a/encoding/hex/hex.ha b/encoding/hex/hex.ha
@@ -10,8 +10,8 @@ use strio;
// characters.
export type invalid = !void;
-// Encodes a byte slice as a hexadecimal string and writes it to a stream.
-export fn encode(sink: *io::stream, b: []u8) (size | io::error) = {
+// Encodes a byte slice as a hexadecimal string and writes it to an I/O handle.
+export fn encode(sink: io::handle, b: []u8) (size | io::error) = {
let z = 0z;
for (let i = 0z; i < len(b); i += 1) {
let s = strconv::u8tosb(b[i], strconv::base::HEX_LOWER);
@@ -67,14 +67,14 @@ export fn decode(s: str) ([]u8 | invalid) = {
decode("this is not hex") as invalid: void;
};
-// Outputs a dump of hex data to a stream alongside the offset and an ASCII
-// representation (if applicable).
+// Outputs a dump of hex data alongside the offset and an ASCII representation
+// (if applicable).
//
// Example output:
//
// 00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
// 00000010 03 00 3e 00 01 00 00 00 80 70 01 00 00 00 00 00 |..>......p......|
-export fn dump(out: *io::stream, data: []u8) (void | io::error) = {
+export fn dump(out: io::handle, data: []u8) (void | io::error) = {
let datalen = len(data): u32;
for (let off = 0u32; off < datalen; off += 16) {
diff --git a/fmt/fmt.ha b/fmt/fmt.ha
@@ -57,14 +57,14 @@ export @noreturn fn fatal(fmt: str, args: field...) void = {
os::exit(1);
};
-// Formats text for printing and writes it to an [[io::stream]], followed by a
+// Formats text for printing and writes it to an [[io::handle]], followed by a
// line feed.
export fn fprintfln(
- s: *io::stream,
+ h: io::handle,
fmt: str,
args: field...
) (io::error | size) = {
- return fprintf(s, fmt, args...)? + io::write(s, ['\n': u32: u8])?;
+ return fprintf(h, fmt, args...)? + io::write(h, ['\n': u32: u8])?;
};
// Formats values for printing using the default format modifiers and writes
@@ -107,20 +107,20 @@ export fn bsprint(buf: []u8, args: formattable...) str = {
};
// Formats values for printing using the default format modifiers and writes
-// them to an [[io::stream]] separated by spaces and followed by a line feed.
-export fn fprintln(s: *io::stream, args: formattable...) (io::error | size) = {
- return fprint(s, args...)? + io::write(s, ['\n': u32: u8])?;
+// them to an [[io::handle]] separated by spaces and followed by a line feed.
+export fn fprintln(h: io::handle, args: formattable...) (io::error | size) = {
+ return fprint(h, args...)? + io::write(h, ['\n': u32: u8])?;
};
// Formats values for printing using the default format modifiers and writes
-// them to an [[io::stream]] separated by spaces.
-export fn fprint(s: *io::stream, args: formattable...) (io::error | size) = {
+// them to an [[io::handle]] separated by spaces.
+export fn fprint(h: io::handle, args: formattable...) (io::error | size) = {
let mod = modifiers { base = strconv::base::DEC, ... };
let n = 0z;
for (let i = 0z; i < len(args); i += 1) {
- n += format(s, args[i], &mod)?;
+ n += format(h, args[i], &mod)?;
if (i != len(args) - 1) {
- n += io::write(s, [' ': u32: u8])?;
+ n += io::write(h, [' ': u32: u8])?;
};
};
return n;
@@ -162,9 +162,9 @@ type paramindex = (uint | nextparam | void);
type nextparam = void;
-// Formats text for printing and writes it to an [[io::stream]].
+// Formats text for printing and writes it to an [[io::handle]].
export fn fprintf(
- s: *io::stream,
+ h: io::handle,
fmt: str,
args: field...
) (io::error | size) = {
@@ -187,7 +187,7 @@ export fn fprintf(
};
let arg = if (r == '{') {
- n += io::write(s, utf8::encoderune('{'))?;
+ n += io::write(h, utf8::encoderune('{'))?;
continue;
} else if (ascii::isdigit(r)) {
strings::push(&iter, r);
@@ -247,7 +247,7 @@ export fn fprintf(
mod.base = strconv::base::DEC;
};
- n += format(s, arg, mod)?;
+ n += format(h, arg, mod)?;
} else if (r == '}') {
match (strings::next(&iter)) {
case void =>
@@ -256,16 +256,20 @@ export fn fprintf(
assert(r == '}', "Invalid format string (hanging '}')");
};
- n += io::write(s, utf8::encoderune('}'))?;
+ n += io::write(h, utf8::encoderune('}'))?;
} else {
- n += io::write(s, utf8::encoderune(r))?;
+ n += io::write(h, utf8::encoderune(r))?;
};
};
return n;
};
-fn format(out: *io::stream, arg: formattable, mod: *modifiers) (size | io::error) = {
+fn format(
+ out: io::handle,
+ arg: formattable,
+ mod: *modifiers,
+) (size | io::error) = {
let z = format_raw(io::empty, arg, mod)?;
let pad: []u8 = [];
@@ -294,7 +298,7 @@ fn format(out: *io::stream, arg: formattable, mod: *modifiers) (size | io::error
};
fn format_raw(
- out: *io::stream,
+ out: io::handle,
arg: formattable,
mod: *modifiers,
) (size | io::error) = {
diff --git a/format/ini/scan.ha b/format/ini/scan.ha
@@ -5,7 +5,7 @@ use io;
use strings;
export type scanner = struct {
- in: *io::stream,
+ in: io::handle,
line: str,
lineno: size,
section: str,
@@ -13,7 +13,7 @@ export type scanner = struct {
// Creates an INI file scanner. Use [[next]] to read entries. The caller must
// call [[finish]] once they're done with this object.
-export fn scan(in: *io::stream) scanner = scanner {
+export fn scan(in: io::handle) scanner = scanner {
in = in,
lineno = 1,
...
@@ -69,7 +69,7 @@ export fn next(sc: *scanner) (entry | io::EOF | error) = {
case idx: size =>
yield idx;
case void =>
- return (sc.in.name, sc.lineno): syntaxerr;
+ return sc.lineno: syntaxerr;
};
free(sc.section);
sc.section = strings::dup(strings::sub(line, 1, end));
@@ -80,7 +80,7 @@ export fn next(sc: *scanner) (entry | io::EOF | error) = {
case idx: size =>
yield idx;
case void =>
- return (sc.in.name, sc.lineno): syntaxerr;
+ return sc.lineno: syntaxerr;
};
return (
sc.section,
diff --git a/format/ini/types.ha b/format/ini/types.ha
@@ -3,7 +3,7 @@ use fmt;
use io;
// A syntax error occured during parsing.
-export type syntaxerr = !(str, size);
+export type syntaxerr = !size;
// Any error that may occur during parsing.
export type error = !(io::error | utf8::invalid | syntaxerr);
@@ -17,5 +17,5 @@ case utf8::invalid =>
case s: syntaxerr =>
// XXX: tuple unpacking could improve this
static let buf: [1024]u8 = [0...];
- yield fmt::bsprintf(buf, "{}:{}: Invalid syntax", s.0, s.1);
+ yield fmt::bsprintf(buf, "{}:{}: Invalid syntax", s: size);
};
diff --git a/format/xml/parser.ha b/format/xml/parser.ha
@@ -10,20 +10,20 @@ use strconv;
use strings;
use strio;
-// Returns an XML parser which reads from a stream. The caller must call
-// [[parser_free]] when they are finished with it.
+// Creates an XML parser. The caller must call [[parser_free]] when they are
+// finished with it.
//
// Hare's XML parser only supports UTF-8 encoded input files.
//
// This function will attempt to read the XML prologue before returning, and
// will return an error if it is not valid.
-export fn parse(in: *io::stream) (*parser | error) = {
+export fn parse(in: io::handle) (*parser | error) = {
// XXX: The main reason we allocate this instead of returning it on the
// 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 {
- orig = in,
in = in,
+ close = false,
namebuf = strio::dynamic(),
entbuf = strio::dynamic(),
textbuf = strio::dynamic(),
@@ -31,15 +31,16 @@ export fn parse(in: *io::stream) (*parser | error) = {
});
if (!bufio::isbuffered(in)) {
par.in = bufio::buffered(par.in, par.buf[..], []);
+ par.close = true;
};
prolog(par)?;
return par;
};
// Frees the resources associated with this parser. Does not close the
-// underlying stream.
+// underlying I/O handle.
export fn parser_free(par: *parser) void = {
- if (par.in != par.orig) {
+ if (par.close) {
io::close(par.in);
};
io::close(par.namebuf);
@@ -358,7 +359,7 @@ fn scan_namedent(par: *parser) (rune | error) = {
return syntaxerr;
};
-fn scan_name(par: *parser, buf: *io::stream) (str | error) = {
+fn scan_name(par: *parser, buf: io::handle) (str | error) = {
strio::reset(buf);
const rn = match (bufio::scanrune(par.in)?) {
diff --git a/format/xml/types.ha b/format/xml/types.ha
@@ -3,16 +3,16 @@ use io;
use os;
export type parser = struct {
- orig: *io::stream,
- in: *io::stream,
+ in: io::handle,
buf: [os::BUFSIZ]u8,
+ close: bool,
state: state,
tags: []str,
// strio buffers:
- namebuf: *io::stream,
- entbuf: *io::stream,
- textbuf: *io::stream,
+ namebuf: io::handle,
+ entbuf: io::handle,
+ textbuf: io::handle,
};
export type state = enum {
diff --git a/fs/fs.ha b/fs/fs.ha
@@ -13,7 +13,7 @@ export fn close(fs: *fs) void = {
// Opens a file. If no flags are provided, the default read/write mode is
// RDONLY.
-export fn open(fs: *fs, path: str, flags: flags...) (*io::stream | error) = {
+export fn open(fs: *fs, path: str, flags: flags...) (io::handle | error) = {
match (fs.open) {
case null =>
return errors::unsupported;
@@ -43,7 +43,7 @@ export fn create(
path: str,
mode: mode,
flags: flags...
-) (*io::stream | error) = {
+) (io::handle | error) = {
match (fs.create) {
case null =>
return errors::unsupported;
diff --git a/fs/mem/+test.ha b/fs/mem/+test.ha
@@ -13,8 +13,7 @@ use strconv;
// fs::create, fs::stat
for (let i = 0z; i < 6; i += 1) {
- let f = fs::create(memfs, names[i], 0, fs::flags::RDWR);
- let f = f as *io::stream;
+ let f = fs::create(memfs, names[i], 0, fs::flags::RDWR)!;
io::write(f, input[i..])!;
io::close(f);
let st = fs::stat(memfs, names[i]) as fs::filestat;
@@ -23,8 +22,7 @@ use strconv;
assert(st.mode & fs::mode::REG == fs::mode::REG);
};
- let f = fs::open(memfs, filename, fs::flags::WRONLY, fs::flags::APPEND);
- let f = f as *io::stream;
+ let f = fs::open(memfs, filename, fs::flags::WRONLY, fs::flags::APPEND)!;
io::write(f, input)!;
io::close(f);
let st = fs::stat(memfs, filename) as fs::filestat;
@@ -41,8 +39,8 @@ use strconv;
fs::remove(memfs, "nonexistent")
as fs::error as errors::noentry: void;
- let f = fs::open(memfs, filename, fs::flags::RDONLY) as *io::stream;
- let f2 = fs::open(memfs, filename, fs::flags::RDONLY) as *io::stream;
+ let f = fs::open(memfs, filename, fs::flags::RDONLY)!;
+ let f2 = fs::open(memfs, filename, fs::flags::RDONLY)!;
let output: [12]u8 = [0...];
assert(io::seek(f2, 3, io::whence::SET) as io::off == 3: io::off);
assert(io::read(f2, output) as size == 9);
@@ -79,10 +77,10 @@ use strconv;
fs::rmdir(memfs, "") as fs::error as errors::invalid: void;
fs::mkdir(memfs, "dir") as void;
- f = fs::create(memfs, "dir/file", 0, fs::flags::WRONLY) as *io::stream;
+ f = fs::create(memfs, "dir/file", 0, fs::flags::WRONLY)!;
assert(io::write(f, input[..]) as size == 6);
io::close(f);
- f = fs::open(memfs, "dir/file", fs::flags::RDONLY) as *io::stream;
+ f = fs::open(memfs, "dir/file", fs::flags::RDONLY)!;
assert(io::read(f, output) as size == 6);
assert(bytes::equal(input, output[..6]));
io::close(f);
@@ -96,7 +94,7 @@ use strconv;
let sub = mksubdir(memfs, "dir") as *fs::fs;
- let f = fs::create(sub, "file", 0, fs::flags::WRONLY) as *io::stream;
+ let f = fs::create(sub, "file", 0, fs::flags::WRONLY)!;
io::write(f, [42])!;
io::close(f);
@@ -104,7 +102,7 @@ use strconv;
assert(sub2 == sub);
fs::close(sub);
- let f = fs::open(sub2, "file", fs::flags::RDONLY) as *io::stream;
+ let f = fs::open(sub2, "file", fs::flags::RDONLY)!;
assert(io::read(f, output) as size == 1);
assert(output[0] == 42);
io::close(f);
@@ -126,8 +124,8 @@ use strconv;
let limit = 32z;
let memfs = memopen();
for (let i = 0z; i < limit; i += 1) {
- let f = fs::create(memfs, strconv::ztos(i), 0, fs::flags::RDWR);
- io::close(f as *io::stream);
+ let f = fs::create(memfs, strconv::ztos(i), 0, fs::flags::RDWR)!;
+ io::close(f);
};
let ino = memfs: *inode;
let dir = ino.data as directory;
diff --git a/fs/mem/mem.ha b/fs/mem/mem.ha
@@ -94,7 +94,7 @@ fn create(
path: str,
mode: fs::mode,
flags: fs::flags...,
-) (*io::stream | fs::error) = {
+) (io::handle | fs::error) = {
let t = file_flags(flags...)?;
let mode = t.0, appnd = t.1;
@@ -116,21 +116,21 @@ fn create(
parent = parent,
});
inode_insert(parent, ino);
- return stream_open(ino, mode, appnd);
+ return stream_open(ino, mode, appnd)?;
};
fn open(
fs: *fs::fs,
path: str,
flags: fs::flags...,
-) (*io::stream | fs::error) = {
+) (io::handle | fs::error) = {
let t = file_flags(flags...)?;
let mode = t.0, appnd = t.1;
let ino = inode_find(fs: *inode, path)?;
if (ino.data is directory) {
return fs::wrongtype;
};
- return stream_open(ino, mode, appnd);
+ return stream_open(ino, mode, appnd)?;
};
fn stat(fs: *fs::fs, path: str) (fs::filestat | fs::error) = {
diff --git a/fs/mem/stream.ha b/fs/mem/stream.ha
@@ -19,7 +19,6 @@ fn stream_open(
let f = ino.data as file;
let s = alloc(stream {
stream = io::stream {
- name = "<fs::mem::stream>",
closer = &stream_close,
seeker = &seek,
...
diff --git a/fs/types.ha b/fs/types.ha
@@ -182,7 +182,7 @@ export type openfunc = fn(
fs: *fs,
path: str,
flags: flags...
-) (*io::stream | error);
+) (io::handle | error);
export type openfilefunc = fn(
fs: *fs,
@@ -195,7 +195,7 @@ export type createfunc = fn(
path: str,
mode: mode,
flags: flags...
-) (*io::stream | error);
+) (io::handle | error);
export type createfilefunc = fn(
fs: *fs,
diff --git a/getopt/getopts.ha b/getopt/getopts.ha
@@ -182,71 +182,71 @@ export fn finish(cmd: *command) void = {
free(cmd.opts);
};
-fn _printusage(s: *io::stream, name: str, indent: bool, help: []help) size = {
- let z = fmt::fprint(s, "Usage:", name) as size;
+fn _printusage(out: io::handle, name: str, indent: bool, help: []help) size = {
+ let z = fmt::fprint(out, "Usage:", name) as size;
let started_flags = false;
for (let i = 0z; i < len(help); i += 1) if (help[i] is flag_help) {
if (!started_flags) {
- z += fmt::fprint(s, " [-") as size;
+ z += fmt::fprint(out, " [-") as size;
started_flags = true;
};
const help = help[i] as flag_help;
- z += fmt::fprint(s, help.0: rune) as size;
+ z += fmt::fprint(out, help.0: rune) as size;
};
if (started_flags) {
- z += fmt::fprint(s, "]") as size;
+ z += fmt::fprint(out, "]") as size;
};
for (let i = 0z; i < len(help); i += 1) if (help[i] is parameter_help) {
const help = help[i] as parameter_help;
if (indent) {
- z += fmt::fprintf(s, "\n\t") as size;
+ z += fmt::fprintf(out, "\n\t") as size;
};
- z += fmt::fprintf(s, " [-{} <{}>]", help.0: rune, help.1) as size;
+ z += fmt::fprintf(out, " [-{} <{}>]", help.0: rune, help.1) as size;
};
let first_arg = true;
for (let i = 1z; i < len(help); i += 1) if (help[i] is cmd_help) {
if (first_arg) {
if (indent) {
- z += fmt::fprintf(s, "\n\t") as size;
+ z += fmt::fprintf(out, "\n\t") as size;
};
first_arg = false;
};
- z += fmt::fprintf(s, " {}", help[i] as cmd_help: str) as size;
+ z += fmt::fprintf(out, " {}", help[i] as cmd_help: str) as size;
};
- return z + fmt::fprint(s, "\n") as size;
+ return z + fmt::fprint(out, "\n") as size;
};
// Prints command usage to the provided stream.
-export fn printusage(s: *io::stream, name: str, help: []help) void = {
+export fn printusage(out: io::handle, name: str, help: []help) void = {
let z = _printusage(io::empty, name, false, help);
- _printusage(s, name, if (z > 72) true else false, help);
+ _printusage(out, name, if (z > 72) true else false, help);
};
// Prints command help to the provided stream.
-export fn printhelp(s: *io::stream, name: str, help: []help) void = {
+export fn printhelp(out: io::handle, name: str, help: []help) void = {
if (help[0] is cmd_help) {
- fmt::fprintfln(s, "{}: {}\n", name, help[0] as cmd_help: str)!;
+ fmt::fprintfln(out, "{}: {}\n", name, help[0] as cmd_help: str)!;
};
- printusage(s, name, help);
+ printusage(out, name, help);
for (let i = 0z; i < len(help); i += 1) match (help[i]) {
case cmd_help => void;
case (flag_help | parameter_help) =>
// Only print this if there are flags to show
- fmt::fprint(s, "\n")!;
+ fmt::fprint(out, "\n")!;
break;
};
for (let i = 0z; i < len(help); i += 1) match (help[i]) {
case cmd_help => void;
case f: flag_help =>
- fmt::fprintfln(s, "-{}: {}", f.0: rune, f.1)!;
+ fmt::fprintfln(out, "-{}: {}", f.0: rune, f.1)!;
case p: parameter_help =>
- fmt::fprintfln(s, "-{} <{}>: {}", p.0: rune, p.1, p.2)!;
+ fmt::fprintfln(out, "-{} <{}>: {}", p.0: rune, p.1, p.2)!;
};
};
diff --git a/hare/lex/README b/hare/lex/README
@@ -1,4 +1,4 @@
-hare::lex provides a lexer for Hare source code. A lexer takes an [[io::stream]]
+hare::lex provides a lexer for Hare source code. A lexer takes an [[io::handle]]
and returns a series of Hare [[token]]s. See the Hare specification for more
details:
diff --git a/hare/lex/lex.ha b/hare/lex/lex.ha
@@ -10,7 +10,7 @@ use strio;
use types;
export type lexer = struct {
- in: *io::stream,
+ in: io::handle,
path: str,
loc: (uint, uint),
un: (token | void),
@@ -47,8 +47,8 @@ export fn strerror(err: error) const str = {
};
};
-// Initializes a new lexer for the given input stream. The path is borrowed.
-export fn init(in: *io::stream, path: str, flags: flags...) lexer = {
+// Initializes a new lexer for the given input. The path is borrowed.
+export fn init(in: io::handle, path: str, flags: flags...) lexer = {
let f = flags::NONE;
for (let i = 0z; i < len(flags); i += 1) {
f |= flags[i];
diff --git a/hare/module/manifest.ha b/hare/module/manifest.ha
@@ -57,7 +57,7 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = {
return manifest;
case err: fs::error =>
return err;
- case file: *io::stream =>
+ case file: io::handle =>
yield file;
};
defer io::close(file);
@@ -282,17 +282,19 @@ export fn manifest_write(ctx: *context, manifest: *manifest) (void | error) = {
defer free(mpath);
let file = temp::named(ctx.fs, cachedir, io::mode::WRITE, 0o644)?;
+ let name = file.1, file = file.0;
defer {
- fs::remove(ctx.fs, file.name): void;
- io::close(&file);
+ fs::remove(ctx.fs, name): void;
+ io::close(file);
+ free(name);
};
let ident = unparse::identstr(manifest.ident);
defer free(ident);
- fmt::fprintfln(&file, "# {}", ident)?;
- fmt::fprintln(&file, "# This file is an internal Hare implementation detail.")?;
- fmt::fprintln(&file, "# The format is not stable.")?;
- fmt::fprintfln(&file, "version {}", VERSION)?;
+ fmt::fprintfln(file, "# {}", ident)?;
+ fmt::fprintln(file, "# This file is an internal Hare implementation detail.")?;
+ fmt::fprintln(file, "# The format is not stable.")?;
+ fmt::fprintfln(file, "version {}", VERSION)?;
for (let i = 0z; i < len(manifest.inputs); i += 1) {
const input = manifest.inputs[i];
let hash = hex::encodestr(input.hash);
@@ -300,7 +302,7 @@ export fn manifest_write(ctx: *context, manifest: *manifest) (void | error) = {
const want = fs::stat_mask::INODE | fs::stat_mask::MTIME;
assert(input.stat.mask & want == want);
- fmt::fprintfln(&file, "input {} {} {} {}",
+ fmt::fprintfln(file, "input {} {} {} {}",
hash, input.path, input.stat.inode,
time::unix(input.stat.mtime))?;
};
@@ -310,19 +312,19 @@ export fn manifest_write(ctx: *context, manifest: *manifest) (void | error) = {
let hash = hex::encodestr(ver.hash);
defer free(hash);
- fmt::fprintf(&file, "module {}", hash)?;
+ fmt::fprintf(file, "module {}", hash)?;
for (let j = 0z; j < len(ver.inputs); j += 1) {
let hash = hex::encodestr(ver.inputs[i].hash);
defer free(hash);
- fmt::fprintf(&file, " {}", hash)?;
+ fmt::fprintf(file, " {}", hash)?;
};
- fmt::fprintln(&file)?;
+ fmt::fprintln(file)?;
};
- fs::move(ctx.fs, file.name, mpath)?;
+ fs::move(ctx.fs, name, mpath)?;
};
fn input_finish(in: *input) void = {
diff --git a/hare/unparse/decl.ha b/hare/unparse/decl.ha
@@ -4,7 +4,7 @@ use hare::ast;
use hare::lex;
use strio;
-export fn decl(out: *io::stream, d: ast::decl) (size | io::error) = {
+export fn decl(out: io::handle, d: ast::decl) (size | io::error) = {
let n = 0z;
if (d.exported) {
n += fmt::fprint(out, "export ")?;
diff --git a/hare/unparse/expr.ha b/hare/unparse/expr.ha
@@ -9,7 +9,7 @@ use hare::lex;
// binary operators (e.g. +) is not accounted for, so such expressions may
// produce a different AST if parsed again.
export fn expr(
- out: *io::stream,
+ out: io::handle,
indent: size,
e: ast::expr
) (size | io::error) = {
@@ -390,7 +390,7 @@ export fn expr(
};
fn constant(
- out: *io::stream,
+ out: io::handle,
indent: size,
e: ast::constant_expr,
) (size | io::error) = {
@@ -439,7 +439,7 @@ fn constant(
};
fn struct_constant(
- out: *io::stream,
+ out: io::handle,
indent: size,
sc: ast::struct_constant,
) (size | io::error) = {
@@ -480,7 +480,7 @@ fn struct_constant(
};
fn for_expr(
- out: *io::stream,
+ out: io::handle,
indent: size,
e: ast::for_expr,
) (size | io::error) = {
@@ -506,7 +506,7 @@ fn for_expr(
};
fn switch_expr(
- out: *io::stream,
+ out: io::handle,
indent: size,
e: ast::switch_expr,
) (size | io::error) = {
@@ -545,7 +545,7 @@ fn switch_expr(
};
fn match_expr(
- out: *io::stream,
+ out: io::handle,
indent: size,
e: ast::match_expr,
) (size | io::error) = {
diff --git a/hare/unparse/ident.ha b/hare/unparse/ident.ha
@@ -4,7 +4,7 @@ use io;
use strio;
// Unparses an identifier.
-export fn ident(out: *io::stream, id: ast::ident) (size | io::error) = {
+export fn ident(out: io::handle, id: ast::ident) (size | io::error) = {
let n = 0z;
for (let i = 0z; i < len(id); i += 1) {
n += fmt::fprintf(out, "{}{}", id[i],
diff --git a/hare/unparse/import.ha b/hare/unparse/import.ha
@@ -4,7 +4,7 @@ use hare::ast;
use strio;
// Unparses an [[ast::import]].
-export fn import(out: *io::stream, i: ast::import) (size | io::error) = {
+export fn import(out: io::handle, i: ast::import) (size | io::error) = {
let n = 0z;
n += fmt::fprint(out, "use ")?;
match (i) {
diff --git a/hare/unparse/type.ha b/hare/unparse/type.ha
@@ -52,7 +52,7 @@ case ast::builtin_type::VOID =>
};
fn prototype(
- out: *io::stream,
+ out: io::handle,
indent: size,
t: ast::func_type,
) (size | io::error) = {
@@ -80,7 +80,7 @@ fn prototype(
};
fn struct_union_type(
- out: *io::stream,
+ out: io::handle,
indent: size,
t: ast::_type,
) (size | io::error) = {
@@ -127,7 +127,7 @@ fn struct_union_type(
// Unparses an [[ast::_type]].
export fn _type(
- out: *io::stream,
+ out: io::handle,
indent: size,
t: ast::_type,
) (size | io::error) = {
diff --git a/hare/unparse/unit.ha b/hare/unparse/unit.ha
@@ -3,7 +3,7 @@ use fmt;
use hare::ast;
// Unparses an [[ast::subunit]].
-export fn subunit(out: *io::stream, s: ast::subunit) (size | io::error) = {
+export fn subunit(out: io::handle, s: ast::subunit) (size | io::error) = {
let n = 0z;
for (let i = 0z; i < len(s.imports); i += 1) {
n += import(out, s.imports[i])?;
diff --git a/hare/unparse/util.ha b/hare/unparse/util.ha
@@ -1,7 +1,7 @@
use io;
use fmt;
-fn newline(out: *io::stream, indent: size) (size | io::error) = {
+fn newline(out: io::handle, indent: size) (size | io::error) = {
let n = 0z;
n += fmt::fprint(out, "\n")?;
for (let i = 0z; i < indent; i += 1) {
diff --git a/io/+linux/file.ha b/io/+linux/file.ha
@@ -7,73 +7,15 @@ use strings;
// [[stream]] in most situations, but there are some APIs which require an
// [[file]] with some OS-level handle backing it - this type is used for such
// APIs.
-export type file = struct {
- stream,
- fd: int,
-};
+export type file = int;
// Opens a Unix file descriptor as a file. This is a low-level interface, to
// open files most programs will use something like [[os::open]]. This function
// is not portable.
-export fn fdopen(fd: int, name: str, mode: mode) file = {
- let stream = file {
- name = name,
- closer = &fd_close_static,
- copier = &fd_copy,
- seeker = &fd_seek,
- fd = fd,
- ...
- };
- if (mode & mode::READ == mode::READ) {
- stream.reader = &fd_read;
- };
- if (mode & mode::WRITE == mode::WRITE) {
- stream.writer = &fd_write;
- };
- return stream;
-};
-
-// Duplicates a [[file]] onto the heap, as if it were opened with [[fdalloc]].
-export fn filedup(f: *file) *file = {
- let new = alloc(*f);
- new.closer = &fd_close;
- return new;
-};
-
-// Similar to [[fdopen]], but heap-allocates the file. Closing the stream will
-// free the associated resources.
-export fn fdalloc(fd: int, name: str, mode: mode) *file =
- filedup(&fdopen(fd, strings::dup(name), mode));
-
-// Returns true if a [[stream]] is a file.
-export fn is_file(s: *stream) bool = {
- return s.reader == &fd_read
- || s.writer == &fd_write
- || s.closer == &fd_close
- || s.copier == &fd_copy;
-};
+export fn fdopen(fd: int) file = fd;
-// Returns the file descriptor for a given [[file]]. This function is not
-// portable.
-export fn fd(f: *file) int = f.fd;
-
-// Returns the file descriptor for a given [[stream]], returning void if the
-// stream is not backed by a file. This function is not portable.
-export fn unwrapfd(s: *stream) (int | void) = {
- for (!is_file(s)) {
- s = match (io::source(s)) {
- case errors::unsupported =>
- return;
- case s: *io::stream =>
- yield s;
- };
- };
- return fd(s: *file);
-};
-
-fn fd_read(s: *stream, buf: []u8) (size | EOF | error) = {
- let stream = s: *file;
- match (rt::read(stream.fd, buf: *[*]u8, len(buf))) {
+fn fd_read(fd: file, buf: []u8) (size | EOF | error) = {
+ match (rt::read(fd, buf: *[*]u8, len(buf))) {
case err: rt::errno =>
return errors::errno(err);
case n: size =>
@@ -86,9 +28,8 @@ fn fd_read(s: *stream, buf: []u8) (size | EOF | error) = {
};
};
-fn fd_write(s: *stream, buf: const []u8) (size | error) = {
- let stream = s: *file;
- match (rt::write(stream.fd, buf: *const [*]u8, len(buf))) {
+fn fd_write(fd: file, buf: const []u8) (size | error) = {
+ match (rt::write(fd, buf: *const [*]u8, len(buf))) {
case err: rt::errno =>
return errors::errno(err);
case n: size =>
@@ -96,61 +37,46 @@ fn fd_write(s: *stream, buf: const []u8) (size | error) = {
};
};
-fn fd_close_static(s: *stream) void = {
- let stream = s: *file;
- rt::close(stream.fd)!;
-};
+fn fd_close(fd: file) void = rt::close(fd)!;
-fn fd_close(s: *stream) void = {
- fd_close_static(s);
- free(s);
+fn fd_seek(
+ fd: file,
+ off: off,
+ whence: whence,
+) (off | error) = {
+ match (rt::lseek(fd, off: i64, whence: uint)) {
+ case err: rt::errno =>
+ return errors::errno(err);
+ case n: i64 =>
+ return n: off;
+ };
};
def SENDFILE_MAX: size = 2147479552z;
-fn fd_copy(to: *stream, from: *stream) (size | error) = {
- if (!is_file(from)) {
- return errors::unsupported;
- };
-
- let to = to: *file, from = from: *file;
+fn fd_copy(to: file, from: file) (size | error) = {
let sum = 0z;
for (true) {
- let n = match (rt::sendfile(to.fd, from.fd,
- null, SENDFILE_MAX)) {
- case err: rt::errno =>
- switch (err) {
- case rt::EINVAL =>
- if (sum == 0) {
- return errors::unsupported;
- };
- return errors::errno(err);
- case =>
- return errors::errno(err);
- };
- case n: size =>
- yield switch (n) {
- case 0 =>
- break;
- case =>
- yield n;
+ let n = match (rt::sendfile(to, from, null, SENDFILE_MAX)) {
+ case err: rt::errno =>
+ switch (err) {
+ case rt::EINVAL =>
+ if (sum == 0) {
+ return errors::unsupported;
};
+ return errors::errno(err);
+ case =>
+ return errors::errno(err);
+ };
+ case n: size =>
+ yield switch (n) {
+ case 0 =>
+ break;
+ case =>
+ yield n;
+ };
};
sum += n;
};
return sum;
};
-
-fn fd_seek(
- s: *stream,
- off: off,
- whence: whence,
-) (off | error) = {
- let stream = s: *file;
- match (rt::lseek(stream.fd, off: i64, whence: uint)) {
- case err: rt::errno =>
- return errors::errno(err);
- case n: i64 =>
- return n: off;
- };
-};
diff --git a/io/+test/stream.ha b/io/+test/stream.ha
@@ -9,7 +9,6 @@ type teststream = struct {
};
fn teststream_open() teststream = teststream {
- name = "teststream",
reader = &teststream_read,
writer = &teststream_write,
...
diff --git a/io/README b/io/README
@@ -0,0 +1,23 @@
+The io module provides input and output (I/O) functionality for Hare programs,
+such as reading from or writing to files. The I/O module is not generally
+responsible for provisioning the I/O objects themselves; see modules like [[os]]
+and [[net]] for this purpose.
+
+I/O operations such as [[read]] or [[write]] accept an I/O handle,
+[[io::handle]], to specify the object of the I/O operation. This type is a
+tagged union of [[io::file]] and *[[io::stream]]. Most programmers should prefer
+to use [[io::handle]] unless they specifically require the special semantics of
+one of its subtypes.
+
+The [[io::file]] type provides access to an object, usually a file descriptor,
+which is provided by the host operating system. It represents objects such as a
+file on disk, an open newtork connection, and so on. The use of [[io::file]] is
+generally required when working with host I/O, such as for modules like
+[[iobus]] or [[unix::poll]].
+
+The [[io::stream]] type is an abstraction that allows Hare programs to implement
+their own I/O objects by providing implementations of [[read]], [[write]], and
+other functions, for an [[io::handle]]. Several standard library modules offer
+implementations of [[io::stream]] for one reason or another, such as [[bufio]].
+Additionally, the io module provides some useful general-purpose I/O streams,
+such as [[io::tee]].
diff --git a/io/copy.ha b/io/copy.ha
@@ -1,15 +1,30 @@
use errors;
-// Copies data from one stream into another. Note that this function will never
-// return if the source stream is infinite.
-export fn copy(dest: *stream, src: *stream) (error | size) = {
+// Copies data from one handle into another. Note that this function will never
+// return if the source handle is infinite.
+export fn copy(dest: handle, src: handle) (error | size) = {
+ match (dest) {
+ case fd: file =>
+ if (src is file) {
+ return fd_copy(fd, src as file);
+ };
+ return copy_fallback(dest, src);
+ case st: *stream =>
+ if (!(src is *stream)) {
+ return copy_fallback(dest, src);
+ };
+ return copy_streams(st, src as *stream);
+ };
+};
+
+fn copy_streams(dest: *stream, src: *stream) (error | size) = {
match (dest.copier) {
case null => void;
case c: *copier =>
match (c(dest, src)) {
case err: error =>
match (err) {
- case errors::unsupported => void; // Use fallback
+ case errors::unsupported => void;
case =>
return err;
};
@@ -17,7 +32,10 @@ export fn copy(dest: *stream, src: *stream) (error | size) = {
return s;
};
};
+ return copy_fallback(dest, src);
+};
+fn copy_fallback(dest: handle, src: handle) (error | size) = {
let w = 0z;
static let buf: [4096]u8 = [0...];
for (true) {
diff --git a/io/empty.ha b/io/empty.ha
@@ -0,0 +1,12 @@
+const _empty: stream = stream {
+ reader = &empty_read,
+ writer = &empty_write,
+ ...
+};
+
+// A [[stream]] which always reads EOF and discards any writes.
+export const empty: *io::stream = &_empty;
+
+fn empty_read(s: *stream, buf: []u8) (size | EOF | error) = EOF;
+
+fn empty_write(s: *stream, buf: const []u8) (size | error) = len(buf);
diff --git a/io/filestream.ha b/io/filestream.ha
@@ -0,0 +1,52 @@
+use errors;
+
+export type filestream = struct {
+ stream,
+ fd: file,
+};
+
+// Creates a [[filestream]] for a [[file]], for compatibility with
+// [[stream]]-oriented APIs. Note that this is generally not thought to be
+// necessary for most programs; most APIs should accept [[handle]] instead of
+// [[stream]] in order to support both file-oriented and stream-oriented I/O.
+export fn fdstream(fd: file) filestream = filestream {
+ reader = &fdstream_read,
+ writer = &fdstream_write,
+ closer = &fdstream_close,
+ seeker = &fdstream_seek,
+ copier = &fdstream_copy,
+};
+
+fn fdstream_read(st: *stream, buf: []u8) (size | EOF | error) = {
+ const st = st: *filestream;
+ assert(st.reader == &fdstream_read);
+ return fd_read(st.fd, buf);
+};
+
+fn fdstream_write(st: *stream, buf: const []u8) (size | error) = {
+ const st = st: *filestream;
+ assert(st.writer == &fdstream_write);
+ return fd_write(st.fd, buf);
+};
+
+fn fdstream_close(st: *stream) void = {
+ const st = st: *filestream;
+ assert(st.closer == &fdstream_close);
+ fd_close(st.fd);
+};
+
+fn fdstream_seek(st: *stream, off: off, whence: whence) (off | error) = {
+ const st = st: *filestream;
+ assert(st.seeker == &fdstream_seek);
+ return fd_seek(st.fd, off, whence);
+};
+
+fn fdstream_copy(to: *stream, from: *stream) (size | error) = {
+ const to = to: *filestream;
+ const from = from: *filestream;
+ assert(to.copier == &fdstream_copy);
+ if (from.copier != &fdstream_copy) {
+ return errors::unsupported;
+ };
+ return fd_copy(to.fd, from.fd);
+};
diff --git a/io/handle.ha b/io/handle.ha
@@ -0,0 +1,57 @@
+// TODO: Examine the ABI constraints of [[handle]]. Would it be better to make
+// stream an integer representing an internal handle into a stream table? This
+// would reduce performance for streams somewhat via the indirect lookup, but
+// improve the ABI performance for files.
+
+// An I/O handle is a resource which I/O operations may be performed on. It is
+// either a [[stream]], which is a userspace I/O abstraction, or a [[file]],
+// which is backed by a resource on the host OS, such as a file descriptor.
+export type handle = (file | *stream);
+
+// Reads up to len(buf) bytes from a [[handle]] into the given buffer, returning
+// the number of bytes read.
+export fn read(h: handle, buf: []u8) (size | EOF | error) = {
+ match (h) {
+ case fd: file =>
+ return fd_read(fd, buf);
+ case st: *stream =>
+ return st_read(st, buf);
+ };
+};
+
+// Writes up to len(buf) bytes to the [[handle]] from the given buffer,
+// returning the number of bytes written.
+export fn write(h: handle, buf: const []u8) (size | error) = {
+ match (h) {
+ case fd: file =>
+ return fd_write(fd, buf);
+ case st: *stream =>
+ return st_write(st, buf);
+ };
+};
+
+// Closes a [[handle]]. No further operations against this handle are permitted
+// after calling this function.
+export fn close(h: handle) void = {
+ match (h) {
+ case fd: file =>
+ fd_close(fd);
+ case st: *stream =>
+ st_close(st);
+ };
+};
+
+// Sets the offset within a [[handle]].
+export fn seek(h: handle, off: off, w: whence) (off | error) = {
+ match (h) {
+ case fd: file =>
+ return fd_seek(fd, off, w);
+ case st: *stream =>
+ return st_seek(st, off, w);
+ };
+};
+
+// Returns the current offset within a [[handle]].
+export fn tell(h: handle) (off | error) = {
+ return seek(h, 0, whence::CUR);
+};
diff --git a/io/limit.ha b/io/limit.ha
@@ -2,14 +2,12 @@ use strings;
export type limitstream = struct {
stream,
- source: *stream,
+ source: handle,
limit: size,
};
-fn limitstream_create(source: *stream, limit: size) limitstream = {
+fn limitstream_create(source: handle, limit: size) limitstream = {
return limitstream {
- name = source.name,
- unwrap = &limit_unwrap,
source = source,
limit = limit,
...
@@ -53,8 +51,3 @@ fn limit_write(s: *stream, buf: const []u8) (size | error) = {
stream.limit -= len(slice);
return write(stream.source, slice);
};
-
-fn limit_unwrap(s: *stream) *io::stream = {
- let s = s: *limitstream;
- return s.source;
-};
diff --git a/io/stream.ha b/io/stream.ha
@@ -1,8 +1,9 @@
use errors;
-// A stream of bytes which supports some subset of read, write, close, or seek
-// operations. To create a custom stream, embed this type as the first member of
-// a struct with user-specific data and fill out these fields as appropriate.
+// A stream of bytes which supports some subset of read, write, close, seek, or
+// copy operations. To create a custom stream, embed this type as the first
+// member of a struct with user-specific data and fill out these fields as
+// appropriate.
//
// export type my_stream = struct {
// io::stream,
@@ -12,7 +13,6 @@ use errors;
// export fn open(path: str) my_stream = {
// let fd = // ...
// return my_stream {
-// name = strings::dup(path),
// reader = &my_stream_read,
// writer = &my_stream_write,
// closer = null,
@@ -24,18 +24,14 @@ use errors;
// let stream = open("example");
// io::read(&stream, buf)!;
export type stream = struct {
- name: str,
reader: nullable *reader,
writer: nullable *writer,
closer: nullable *closer,
- copier: nullable *copier,
seeker: nullable *seeker,
- unwrap: nullable *unwrap,
+ copier: nullable *copier,
};
-// Reads up to len(buf) bytes from the reader into the given buffer, returning
-// the number of bytes read.
-export fn read(s: *stream, buf: []u8) (size | EOF | error) = {
+fn st_read(s: *stream, buf: []u8) (size | EOF | error) = {
match (s.reader) {
case null =>
return errors::unsupported;
@@ -44,9 +40,7 @@ export fn read(s: *stream, buf: []u8) (size | EOF | error) = {
};
};
-// Writes up to len(buf) bytes to the stream from the given buffer, returning
-// the number of bytes written.
-export fn write(s: *stream, buf: const []u8) (size | error) = {
+fn st_write(s: *stream, buf: const []u8) (size | error) = {
match (s.writer) {
case null =>
return errors::unsupported;
@@ -55,8 +49,7 @@ export fn write(s: *stream, buf: const []u8) (size | error) = {
};
};
-// Closes the stream.
-export fn close(s: *stream) void = {
+fn st_close(s: *stream) void = {
match (s.closer) {
case null => void;
case c: *closer =>
@@ -64,8 +57,7 @@ export fn close(s: *stream) void = {
};
};
-// Sets the offset within the stream.
-export fn seek(s: *stream, off: off, w: whence) (off | error) = {
+fn st_seek(s: *stream, off: off, w: whence) (off | error) = {
match (s.seeker) {
case null =>
return errors::unsupported;
@@ -73,36 +65,3 @@ export fn seek(s: *stream, off: off, w: whence) (off | error) = {
return sk(s, off, w);
};
};
-
-// Returns the current offset within the stream.
-export fn tell(s: *stream) (off | error) = {
- match (s.seeker) {
- case null =>
- return errors::unsupported;
- case sk: *seeker =>
- return sk(s, 0, whence::CUR);
- };
-};
-
-// Returns the underlying stream for a stream which wraps another stream.
-export fn source(s: *stream) (*io::stream | errors::unsupported) = {
- match (s.unwrap) {
- case null =>
- return errors::unsupported;
- case uw: *unwrap =>
- return uw(s);
- };
-};
-
-let _empty: io::stream = io::stream {
- reader = &empty_read,
- writer = &empty_write,
- ...
-};
-
-// A [[stream]] which always reads EOF and discards any writes.
-export let empty: *io::stream = &_empty;
-
-fn empty_read(s: *stream, buf: []u8) (size | EOF | error) = EOF;
-
-fn empty_write(s: *stream, buf: const []u8) (size | error) = len(buf);
diff --git a/io/tee.ha b/io/tee.ha
@@ -1,16 +1,15 @@
export type teestream = struct {
stream,
- source: *stream,
- sink: *stream,
+ source: handle,
+ sink: handle,
};
// Creates a reader which copies reads into a sink before forwarding them to the
// caller. This stream does not need to be closed, and closing it will not close
// the secondary streams.
-export fn tee(source: *stream, sink: *stream) teestream = {
+export fn tee(source: handle, sink: handle) teestream = {
return teestream {
reader = &tee_read,
- unwrap = &tee_unwrap,
source = source,
sink = sink,
...
@@ -30,9 +29,3 @@ fn tee_read(s: *stream, buf: []u8) (size | EOF | error) = {
};
return z;
};
-
-fn tee_unwrap(s: *stream) *io::stream = {
- assert(s.unwrap == &tee_unwrap);
- let s = s: *teestream;
- return s.source;
-};
diff --git a/net/+linux.ha b/net/+linux.ha
@@ -30,11 +30,7 @@ export fn stream_accept(l: *listener) (io::file | error) = {
case fd: int =>
yield fd;
};
-
- static let namebuf: [32]u8 = [0...];
- return io::fdopen(fd,
- fmt::bsprintf(namebuf, "<network fd {}>", fd),
- io::mode::READ | io::mode::WRITE);
+ return io::fdopen(fd);
};
export fn stream_shutdown(l: *listener) void = {
diff --git a/net/dns/query.ha b/net/dns/query.ha
@@ -27,17 +27,17 @@ export fn query(query: *message, servers: ip::addr...) (*message | error) = {
};
let socket4 = udp::listen(ip::ANY_V4, 0)?;
- defer io::close(&socket4);
+ defer io::close(socket4);
let socket6 = udp::listen(ip::ANY_V6, 0)?;
- defer io::close(&socket6);
+ defer io::close(socket6);
const pollfd: [_]poll::pollfd = [
poll::pollfd {
- fd = io::fd(&socket4),
+ fd = socket4,
events = poll::event::POLLIN,
...
},
poll::pollfd {
- fd = io::fd(&socket6),
+ fd = socket6,
events = poll::event::POLLIN,
...
},
@@ -50,9 +50,9 @@ export fn query(query: *message, servers: ip::addr...) (*message | error) = {
// first one which sends us a reasonable answer.
for (let i = 0z; i < len(servers); i += 1) match (servers[i]) {
case ip::addr4 =>
- udp::sendto(&socket4, sendbuf[..z], servers[i], 53)?;
+ udp::sendto(socket4, sendbuf[..z], servers[i], 53)?;
case ip::addr6 =>
- udp::sendto(&socket6, sendbuf[..z], servers[i], 53)?;
+ udp::sendto(socket6, sendbuf[..z], servers[i], 53)?;
};
let header = header { ... };
@@ -65,10 +65,10 @@ export fn query(query: *message, servers: ip::addr...) (*message | error) = {
let src: ip::addr = ip::ANY_V4;
if (pollfd[0].revents & poll::event::POLLIN != 0) {
- z = udp::recvfrom(&socket4, recvbuf, &src, null)?;
+ z = udp::recvfrom(socket4, recvbuf, &src, null)?;
};
if (pollfd[1].revents & poll::event::POLLIN != 0) {
- z = udp::recvfrom(&socket6, recvbuf, &src, null)?;
+ z = udp::recvfrom(socket6, recvbuf, &src, null)?;
};
let expected = false;
diff --git a/net/ip/ip.ha b/net/ip/ip.ha
@@ -151,7 +151,7 @@ export fn parse(s: str) (addr | invalid) = {
return invalid;
};
-fn fmtv4(s: *io::stream, a: addr4) (io::error | size) = {
+fn fmtv4(s: io::handle, a: addr4) (io::error | size) = {
let ret = 0z;
for (let i = 0; i < 4; i += 1) {
if (i > 0) {
@@ -162,7 +162,7 @@ fn fmtv4(s: *io::stream, a: addr4) (io::error | size) = {
return ret;
};
-fn fmtv6(s: *io::stream, a: addr6) (io::error | size) = {
+fn fmtv6(s: io::handle, a: addr6) (io::error | size) = {
let ret = 0z;
let zstart: int = -1;
let zend: int = -1;
@@ -283,7 +283,7 @@ fn masklen(addr: []u8) (void | size) = {
return n;
};
-fn fmtmask(s: *io::stream, mask: addr) (io::error | size) = {
+fn fmtmask(s: io::handle, mask: addr) (io::error | size) = {
let ret = 0z;
let slice = match (mask) {
case v4: addr4 =>
@@ -305,7 +305,7 @@ fn fmtmask(s: *io::stream, mask: addr) (io::error | size) = {
return ret;
};
-fn fmtsubnet(s: *io::stream, subnet: subnet) (io::error | size) = {
+fn fmtsubnet(s: io::handle, subnet: subnet) (io::error | size) = {
let ret = 0z;
ret += fmt(s, subnet.addr)?;
ret += fmt::fprintf(s, "/")?;
@@ -314,7 +314,7 @@ fn fmtsubnet(s: *io::stream, subnet: subnet) (io::error | size) = {
};
// Formats an [[addr]] or [[subnet]] and prints it to a stream.
-export fn fmt(s: *io::stream, item: (...addr | subnet)) (io::error | size) = {
+export fn fmt(s: io::handle, item: (...addr | subnet)) (io::error | size) = {
match (item) {
case v4: addr4 =>
return fmtv4(s, v4)?;
diff --git a/net/tcp/+linux.ha b/net/tcp/+linux.ha
@@ -38,8 +38,7 @@ export fn connect(
return errors::errno(err);
case int => void;
};
- return io::fdopen(sockfd, ip::string(addr),
- io::mode::READ | io::mode::WRITE);
+ return io::fdopen(sockfd);
};
// Binds a TCP listener to the given address.
@@ -119,11 +118,10 @@ export fn listen(
// Returns the remote address for a given connection, or void if none is
// available.
-export fn peeraddr(peer: *io::file) ((ip::addr, u16) | void) = {
- let fd = io::fd(peer);
+export fn peeraddr(peer: io::file) ((ip::addr, u16) | void) = {
let sn = rt::sockaddr {...};
let sz = size(rt::sockaddr): u32;
- if (rt::getpeername(fd, &sn, &sz) is rt::errno) {
+ if (rt::getpeername(peer, &sn, &sz) is rt::errno) {
return;
};
return ip::from_native(sn);
diff --git a/net/udp/+linux.ha b/net/udp/+linux.ha
@@ -27,8 +27,7 @@ export fn connect(
const sz = size(rt::sockaddr): u32;
match (rt::connect(sockfd, &sockaddr, sz)) {
case int =>
- return io::fdopen(sockfd, "<udp connection>",
- io::mode::READ | io::mode::WRITE);
+ return io::fdopen(sockfd);
case err: rt::errno =>
return errors::errno(err);
};
@@ -76,13 +75,12 @@ export fn listen(
*options[i] = addr.1;
};
- return io::fdopen(sockfd, "<udp listener>",
- io::mode::READ | io::mode::WRITE);
+ return io::fdopen(sockfd);
};
// Sends a UDP packet to the destination previously specified by [[connect]].
-export fn send(sock: *io::file, buf: []u8) (size | net::error) = {
- match (rt::send(io::fd(sock), buf: *[*]u8, len(buf), 0)) {
+export fn send(sock: io::file, buf: []u8) (size | net::error) = {
+ match (rt::send(sock, buf: *[*]u8, len(buf), 0)) {
case sz: size =>
return sz;
case err: rt::errno =>
@@ -92,15 +90,14 @@ export fn send(sock: *io::file, buf: []u8) (size | net::error) = {
// Sends a UDP packet using this socket.
export fn sendto(
- sock: *io::file,
+ sock: io::file,
buf: []u8,
dest: ip::addr,
port: u16,
) (size | net::error) = {
const sockaddr = ip::to_native(dest, port);
const sz = size(rt::sockaddr): u32;
- match (rt::sendto(io::fd(sock), buf: *[*]u8, len(buf),
- 0, &sockaddr, sz)) {
+ match (rt::sendto(sock, buf: *[*]u8, len(buf), 0, &sockaddr, sz)) {
case sz: size =>
return sz;
case err: rt::errno =>
@@ -110,15 +107,15 @@ export fn sendto(
// Receives a UDP packet from a bound socket.
export fn recvfrom(
- sock: *io::file,
+ sock: io::file,
buf: []u8,
src: nullable *ip::addr,
port: nullable *u16,
) (size | net::error) = {
let addrsz = size(rt::sockaddr): u32;
const sockaddr = rt::sockaddr { ... };
- const sz = match (rt::recvfrom(io::fd(sock), buf: *[*]u8, len(buf),
- 0, &sockaddr, &addrsz)) {
+ const sz = match (rt::recvfrom(sock, buf: *[*]u8, len(buf), 0,
+ &sockaddr, &addrsz)) {
case sz: size =>
yield sz;
case err: rt::errno =>
diff --git a/net/unix/+linux.ha b/net/unix/+linux.ha
@@ -32,9 +32,7 @@ export fn connect(addr: addr) (io::file | net::error) = {
case int => void;
};
static let buf: [rt::UNIX_PATH_MAX + 32]u8 = [0...];
- return io::fdopen(sockfd,
- fmt::bsprintf(buf, "<unix connection {}>", addr),
- io::mode::READ | io::mode::WRITE);
+ return io::fdopen(sockfd);
};
// Binds a UNIX socket listener to the given path.
diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha
@@ -51,7 +51,7 @@ type os_filesystem = struct {
// a dirfdfs, [[fs::flags::NOCTTY]] and [[fs::flags::CLOEXEC]] are used when opening
// the file. If you pass your own flags, it is recommended that you add these
// unless you know that you do not want them.
-export fn dirfdopen(fd: int, resolve: resolve_flags...) *fs::fs = {
+export fn dirfdopen(fd: io::file, resolve: resolve_flags...) *fs::fs = {
let ofs = alloc(os_filesystem { ... });
let fs = static_dirfdopen(fd, ofs);
for (let i = 0z; i < len(resolve); i += 1) {
@@ -61,7 +61,7 @@ export fn dirfdopen(fd: int, resolve: resolve_flags...) *fs::fs = {
return fs;
};
-fn static_dirfdopen(fd: int, filesystem: *os_filesystem) *fs::fs = {
+fn static_dirfdopen(fd: io::file, filesystem: *os_filesystem) *fs::fs = {
*filesystem = os_filesystem {
fs = fs::fs {
open = &fs_open,
@@ -158,7 +158,7 @@ fn _fs_open(
yield fd;
};
- return io::fdopen(fd, path, mode);
+ return io::fdopen(fd);
};
fn fs_open_file(
@@ -202,7 +202,7 @@ fn fs_open(
fs: *fs::fs,
path: str,
flags: fs::flags...
-) (*io::stream | fs::error) = io::filedup(&fs_open_file(fs, path, flags...)?);
+) (io::handle | fs::error) = fs_open_file(fs, path, flags...)?;
fn fs_create_file(
fs: *fs::fs,
@@ -243,8 +243,8 @@ fn fs_create(
path: str,
mode: fs::mode,
flags: fs::flags...
-) (*io::stream | fs::error) = {
- return io::filedup(&fs_create_file(fs, path, mode, flags...)?);
+) (io::handle | fs::error) = {
+ return fs_create_file(fs, path, mode, flags...)?;
};
fn fs_remove(fs: *fs::fs, path: str) (void | fs::error) = {
diff --git a/os/+linux/stdfd.ha b/os/+linux/stdfd.ha
@@ -1,29 +1,40 @@
use bufio;
use io;
-let static_stdin_fd: io::file = io::file { ... };
-let static_stdout_fd: io::file = io::file { ... };
-let static_stderr_fd: io::file = io::file { ... };
+let static_stdin_bufio: bufio::bufstream = bufio::bufstream {
+ source = 0,
+ ...
+};
+
+let static_stdout_bufio: bufio::bufstream = bufio::bufstream {
+ source = 1,
+ ...
+};
+
+// The standard input. This handle is buffered.
+export let stdin: io::handle = 0;
-let static_stdin_bufio: bufio::bufstream = bufio::bufstream { ... };
-let static_stdout_bufio: bufio::bufstream = bufio::bufstream { ... };
+// The standard input, as an [[io::file]]. This handle is unbuffered.
+export let stdin_file: io::file = 0;
+
+// The standard output. This handle is buffered.
+export let stdout: io::handle = 1;
+
+// The standard output, as an [[io::file]]. This handle is unbuffered.
+export let stdout_file: io::file = 1;
+
+// The standard error.
+export let stderr: io::file = 2;
// The recommended buffer size for reading from disk.
export def BUFSIZ: size = 4096; // 4 KiB
@init fn init_stdfd() void = {
- static_stdin_fd = io::fdopen(0, "<stdin>", io::mode::READ);
- static_stdout_fd = io::fdopen(1, "<stdout>", io::mode::WRITE);
- static_stderr_fd = io::fdopen(2, "<stderr>", io::mode::WRITE);
- stdin = &static_stdin_fd;
- stdout = &static_stdout_fd;
- stderr = &static_stderr_fd;
-
static let stdinbuf: [BUFSIZ]u8 = [0...];
- stdin = bufio::static_buffered(stdin, stdinbuf, [], &static_stdin_bufio);
+ stdin = bufio::static_buffered(0, stdinbuf, [], &static_stdin_bufio);
static let stdoutbuf: [BUFSIZ]u8 = [0...];
- stdout = bufio::static_buffered(stdout, [], stdoutbuf, &static_stdout_bufio);
+ stdout = bufio::static_buffered(1, [], stdoutbuf, &static_stdout_bufio);
};
@fini fn fini_stdfd() void = {
diff --git a/os/stdfd.ha b/os/stdfd.ha
@@ -1,10 +0,0 @@
-use io;
-
-// The standard input.
-export let stdin: *io::stream = null: *io::stream;
-
-// The standard output.
-export let stdout: *io::stream = null: *io::stream;
-
-// The standard error.
-export let stderr: *io::stream = null: *io::stream;
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -547,6 +547,9 @@ gensrcs_io() {
'arch$(ARCH).ha' \
copy.ha \
drain.ha \
+ empty.ha \
+ filestream.ha \
+ handle.ha \
limit.ha \
'println$(PLATFORM).ha' \
stream.ha \
@@ -706,7 +709,6 @@ os() {
'$(PLATFORM)/dirfdfs.ha' \
'$(PLATFORM)/stdfd.ha' \
'$(PLATFORM)/fs.ha' \
- stdfd.ha \
fs.ha
gen_ssa os io strings types fs encoding::utf8 bytes bufio errors
}
@@ -817,7 +819,7 @@ time() {
temp() {
gen_srcs temp \
'$(PLATFORM).ha'
- gen_ssa temp crypto::random encoding::hex fs io os path strio fmt
+ gen_ssa temp crypto::random encoding::hex fs io os path strio fmt strings
}
gensrcs_types() {
diff --git a/stdlib.mk b/stdlib.mk
@@ -821,6 +821,9 @@ stdlib_io_srcs= \
$(STDLIB)/io/arch$(ARCH).ha \
$(STDLIB)/io/copy.ha \
$(STDLIB)/io/drain.ha \
+ $(STDLIB)/io/empty.ha \
+ $(STDLIB)/io/filestream.ha \
+ $(STDLIB)/io/handle.ha \
$(STDLIB)/io/limit.ha \
$(STDLIB)/io/println$(PLATFORM).ha \
$(STDLIB)/io/stream.ha \
@@ -1005,7 +1008,6 @@ stdlib_os_srcs= \
$(STDLIB)/os/$(PLATFORM)/dirfdfs.ha \
$(STDLIB)/os/$(PLATFORM)/stdfd.ha \
$(STDLIB)/os/$(PLATFORM)/fs.ha \
- $(STDLIB)/os/stdfd.ha \
$(STDLIB)/os/fs.ha
$(HARECACHE)/os/os.ssa: $(stdlib_os_srcs) $(stdlib_rt) $(stdlib_io) $(stdlib_strings) $(stdlib_types) $(stdlib_fs) $(stdlib_encoding_utf8) $(stdlib_bytes) $(stdlib_bufio) $(stdlib_errors)
@@ -1117,7 +1119,7 @@ $(HARECACHE)/strio/strio.ssa: $(stdlib_strio_srcs) $(stdlib_rt) $(stdlib_io) $(s
stdlib_temp_srcs= \
$(STDLIB)/temp/$(PLATFORM).ha
-$(HARECACHE)/temp/temp.ssa: $(stdlib_temp_srcs) $(stdlib_rt) $(stdlib_crypto_random) $(stdlib_encoding_hex) $(stdlib_fs) $(stdlib_io) $(stdlib_os) $(stdlib_path) $(stdlib_strio) $(stdlib_fmt)
+$(HARECACHE)/temp/temp.ssa: $(stdlib_temp_srcs) $(stdlib_rt) $(stdlib_crypto_random) $(stdlib_encoding_hex) $(stdlib_fs) $(stdlib_io) $(stdlib_os) $(stdlib_path) $(stdlib_strio) $(stdlib_fmt) $(stdlib_strings)
@printf 'HAREC \t$@\n'
@mkdir -p $(HARECACHE)/temp
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ntemp \
@@ -2072,6 +2074,9 @@ testlib_io_srcs= \
$(STDLIB)/io/arch$(ARCH).ha \
$(STDLIB)/io/copy.ha \
$(STDLIB)/io/drain.ha \
+ $(STDLIB)/io/empty.ha \
+ $(STDLIB)/io/filestream.ha \
+ $(STDLIB)/io/handle.ha \
$(STDLIB)/io/limit.ha \
$(STDLIB)/io/println$(PLATFORM).ha \
$(STDLIB)/io/stream.ha \
@@ -2261,7 +2266,6 @@ testlib_os_srcs= \
$(STDLIB)/os/$(PLATFORM)/dirfdfs.ha \
$(STDLIB)/os/$(PLATFORM)/stdfd.ha \
$(STDLIB)/os/$(PLATFORM)/fs.ha \
- $(STDLIB)/os/stdfd.ha \
$(STDLIB)/os/fs.ha
$(TESTCACHE)/os/os.ssa: $(testlib_os_srcs) $(testlib_rt) $(testlib_io) $(testlib_strings) $(testlib_types) $(testlib_fs) $(testlib_encoding_utf8) $(testlib_bytes) $(testlib_bufio) $(testlib_errors)
@@ -2376,7 +2380,7 @@ $(TESTCACHE)/strio/strio.ssa: $(testlib_strio_srcs) $(testlib_rt) $(testlib_io)
testlib_temp_srcs= \
$(STDLIB)/temp/$(PLATFORM).ha
-$(TESTCACHE)/temp/temp.ssa: $(testlib_temp_srcs) $(testlib_rt) $(testlib_crypto_random) $(testlib_encoding_hex) $(testlib_fs) $(testlib_io) $(testlib_os) $(testlib_path) $(testlib_strio) $(testlib_fmt)
+$(TESTCACHE)/temp/temp.ssa: $(testlib_temp_srcs) $(testlib_rt) $(testlib_crypto_random) $(testlib_encoding_hex) $(testlib_fs) $(testlib_io) $(testlib_os) $(testlib_path) $(testlib_strio) $(testlib_fmt) $(testlib_strings)
@printf 'HAREC \t$@\n'
@mkdir -p $(TESTCACHE)/temp
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ntemp \
diff --git a/strio/README b/strio/README
@@ -1,5 +1,5 @@
strio provides string-related I/O operations, specifically to make efficient use
of memory while building strings incrementally. The main entry points to strio
are [[dynamic]] and [[fixed]]. Each of the utility functions (e.g.
-[[appendrune]]) work correctly with any [[io::stream]], but for efficiency
+[[appendrune]]) work correctly with any [[io::handle]], but for efficiency
reasons it is recommended that they are either a strio or [[bufio]] stream.
diff --git a/strio/dynamic.ha b/strio/dynamic.ha
@@ -13,10 +13,9 @@ type dynamic_stream = struct {
// Calling [[io::close]] on this stream will free the buffer. Call [[strio::finish]]
// instead to free up resources associated with the stream, but transfer
// ownership of the buffer to the caller.
-export fn dynamic() *io::stream = {
+export fn dynamic() io::handle = {
let s = alloc(dynamic_stream {
stream = io::stream {
- name = "<strio::dynamic>",
writer = &dynamic_write,
closer = &dynamic_close,
...
@@ -26,12 +25,21 @@ export fn dynamic() *io::stream = {
return &s.stream;
};
+fn get_dynamic_stream(in: io::handle) *dynamic_stream = {
+ match (in) {
+ case io::file =>
+ abort("Invalid use of strio on io::file");
+ case st: *io::stream =>
+ assert(st.writer == &dynamic_write,
+ "Invalid use of strio on non-strio I/O stream");
+ return st: *dynamic_stream;
+ };
+};
+
// Closes the stream without freeing the buffer, instead transferring ownership
// of it to the caller.
-export fn finish(s: *io::stream) str = {
- assert(s.writer == &dynamic_write,
- "strio::finish called on non-strio::dynamic stream");
- let s = s: *dynamic_stream;
+export fn finish(in: io::handle) str = {
+ let s = get_dynamic_stream(in);
let buf = s.buf;
free(s);
return strings::fromutf8(buf);
@@ -39,19 +47,15 @@ export fn finish(s: *io::stream) str = {
// Resets the buffer's length to zero, but keeps the allocated memory around for
// future writes.
-export fn reset(s: *io::stream) void = {
- assert(s.writer == &dynamic_write,
- "strio::reset called on non-strio::dynamic stream");
- const s = s: *dynamic_stream;
+export fn reset(in: io::handle) void = {
+ const s = get_dynamic_stream(in);
s.buf = s.buf[..0];
};
// Truncates the buffer, freeing memory associated with it and setting its
// length to zero.
-export fn truncate(s: *io::stream) void = {
- assert(s.writer == &dynamic_write,
- "strio::truncate called on non-strio::dynamic stream");
- let s = s: *dynamic_stream;
+export fn truncate(in: io::handle) void = {
+ let s = get_dynamic_stream(in);
delete(s.buf[..]);
};
diff --git a/strio/fixed.ha b/strio/fixed.ha
@@ -9,10 +9,9 @@ type fixed_stream = struct {
// Creates a write-only string stream using the provided buffer for storage.
// The program aborts if writes would exceed the buffer's capacity.
-export fn fixed(in: []u8) *io::stream = {
+export fn fixed(in: []u8) io::handle = {
let s = alloc(fixed_stream {
stream = io::stream {
- name = "<strio::fixed>",
writer = &fixed_write,
closer = &fixed_close,
...
@@ -25,15 +24,21 @@ export fn fixed(in: []u8) *io::stream = {
// Returns the current contents of the buffer as a string. Aborts the program if
// invalid UTF-8 has been written to the buffer.
-export fn string(s: *io::stream) str = {
- if (s.writer == &fixed_write) {
- let stream = s: *fixed_stream;
+export fn string(in: io::handle) str = {
+ let stream = match (in) {
+ case io::file =>
+ abort("Invalid use of strio with io::file");
+ case st: *io::stream =>
+ yield st;
+ };
+
+ if (stream.writer == &fixed_write) {
+ let stream = stream: *fixed_stream;
const n = len(stream.buf) - len(stream.cur);
return strings::fromutf8(stream.buf[..n]);
- } else if (s.writer == &dynamic_write) {
- let s = s: *dynamic_stream;
- let buf = s.buf;
- return strings::fromutf8(buf);
+ } else if (stream.writer == &dynamic_write) {
+ let stream = stream: *dynamic_stream;
+ return strings::fromutf8(stream.buf);
} else {
abort("strio::string called on non-strio stream");
};
diff --git a/strio/ops.ha b/strio/ops.ha
@@ -2,16 +2,16 @@ use encoding::utf8;
use io;
use strings;
-// Appends zero or more strings to an [[io::stream]]. The stream needn't be a
+// Appends zero or more strings to an [[io::handle]]. The output needn't be a
// strio stream, but it's generally more efficient if it is. Returns the number
// of bytes written, or an error.
-export fn concat(st: *io::stream, strs: str...) (size | io::error) = {
+export fn concat(out: io::handle, strs: str...) (size | io::error) = {
let n = 0z;
for (let i = 0z; i < len(strs); i += 1) {
let q = 0z;
let buf = strings::toutf8(strs[i]);
for (q < len(buf)) {
- let w = io::write(st, buf[q..])?;
+ let w = io::write(out, buf[q..])?;
n += w;
q -= w;
};
@@ -27,24 +27,24 @@ export fn concat(st: *io::stream, strs: str...) (size | io::error) = {
assert(string(st) == "hello world");
};
-// Joins several strings together by a delimiter and writes them to a stream.
-// The stream needn't be a strio stream, but it's generally more efficient if it
-// is. Returns the number of bytes written, or an error.
-export fn join(st: *io::stream, delim: str, strs: str...) (size | io::error) = {
+// Joins several strings together by a delimiter and writes them to a handle.
+// The output needn't be a strio stream, but it's generally more efficient if it
+// is. Returns the number of bytes written, or an error.
+export fn join(out: io::handle, delim: str, strs: str...) (size | io::error) = {
let n = 0z;
let delim = strings::toutf8(delim);
for (let i = 0z; i < len(strs); i += 1) {
let q = 0z;
let buf = strings::toutf8(strs[i]);
for (q < len(buf)) {
- let w = io::write(st, buf[q..])?;
+ let w = io::write(out, buf[q..])?;
n += w;
q -= w;
};
if (i + 1 < len(strs)) {
let q = 0z;
for (q < len(delim)) {
- let w = io::write(st, delim[q..])?;
+ let w = io::write(out, delim[q..])?;
n += w;
q -= w;
};
@@ -66,24 +66,24 @@ export fn join(st: *io::stream, delim: str, strs: str...) (size | io::error) = {
assert(string(st) == "foo");
};
-// Joins several strings together by a delimiter and writes them to a stream, in
-// reverse order. The stream needn't be a strio stream, but it's generally more
+// Joins several strings together by a delimiter and writes them to a handle, in
+// reverse order. The output needn't be a strio stream, but it's generally more
// efficient if it is. Returns the number of bytes written, or an error.
-export fn rjoin(st: *io::stream, delim: str, strs: str...) (size | io::error) = {
+export fn rjoin(out: io::handle, delim: str, strs: str...) (size | io::error) = {
let n = 0z;
let delim = strings::toutf8(delim);
for (let i = len(strs); i > 0; i -= 1) {
let q = 0z;
let buf = strings::toutf8(strs[i - 1]);
for (q < len(buf)) {
- let w = io::write(st, buf[q..])?;
+ let w = io::write(out, buf[q..])?;
n += w;
q -= w;
};
if (i - 1 > 0) {
let q = 0z;
for (q < len(delim)) {
- let w = io::write(st, delim[q..])?;
+ let w = io::write(out, delim[q..])?;
n += w;
q -= w;
};
@@ -106,5 +106,5 @@ export fn rjoin(st: *io::stream, delim: str, strs: str...) (size | io::error) =
};
// Appends a rune to a stream.
-export fn appendrune(st: *io::stream, r: rune) (size | io::error) =
- io::write(st, utf8::encoderune(r));
+export fn appendrune(out: io::handle, r: rune) (size | io::error) =
+ io::write(out, utf8::encoderune(r));
diff --git a/temp/+linux.ha b/temp/+linux.ha
@@ -7,6 +7,7 @@ use io;
use os;
use path;
use strio;
+use strings;
fn get_tmpdir() str = os::tryenv("TMPDIR", "/tmp");
@@ -35,16 +36,17 @@ export fn file(
// TODO: Add a custom "close" function which removes the named file
match (os::create(get_tmpdir(), fmode, oflags)) {
case err: fs::error =>
- return named(os::cwd, get_tmpdir(), iomode, mode...);
+ let file = named(os::cwd, get_tmpdir(), iomode, mode...)?;
+ free(file.1);
+ return file.0;
case f: io::file =>
return f;
};
};
// Creates a named temporary file in the given directory of the given
-// filesystem. The path to the temporary file may be found via the name field of
-// [[io::stream]]. The caller is responsible for removing the file when they're
-// done with it.
+// filesystem. The caller is responsible for removing the file and freeing the
+// name when they're done with it.
//
// The I/O mode must be either [[io::mode::WRITE]] or [[io::mode::RDWR]].
//
@@ -55,7 +57,7 @@ export fn named(
path: str,
iomode: io::mode,
mode: fs::mode...
-) (io::file | fs::error) = {
+) ((io::file, str) | fs::error) = {
assert(iomode == io::mode::WRITE || iomode == io::mode::RDWR);
assert(len(mode) == 0 || len(mode) == 1);
@@ -80,7 +82,7 @@ export fn named(
case err: fs::error =>
return err;
case f: io::file =>
- return f;
+ return (f, strings::dup(fpath));
};
};
abort(); // Unreachable
diff --git a/unix/hosts/lookup.ha b/unix/hosts/lookup.ha
@@ -13,11 +13,11 @@ export fn lookup(name: str) []ip::addr = {
// XXX: Would be cool if we could do this without allocating anything
// XXX: Would be cool to have meaningful error handling(?)
const file = os::open(path)!;
- defer io::close(&file);
+ defer io::close(file);
let addrs: []ip::addr = [];
for (true) {
- const line = match (bufio::scanline(&file)) {
+ const line = match (bufio::scanline(file)) {
case io::EOF =>
break;
case line: []u8 =>
diff --git a/unix/passwd/group.ha b/unix/passwd/group.ha
@@ -16,10 +16,10 @@ export type grent = struct {
userlist: []str,
};
-// Reads a Unix-like group entry from a stream. The caller must free the result
-// using [[grent_finish]].
-export fn nextgr(stream: *io::stream) (grent | io::EOF | io::error | invalid) = {
- let line = match (bufio::scanline(stream)?) {
+// Reads a Unix-like group entry from an [[io::handle]]. The caller must free
+// the result using [[grent_finish]].
+export fn nextgr(in: io::handle) (grent | io::EOF | io::error | invalid) = {
+ let line = match (bufio::scanline(in)?) {
case ln: []u8 =>
yield ln;
case io::EOF =>
@@ -72,10 +72,10 @@ export fn getgroup(name: str) (grent | void) = {
case =>
abort("Unable to open /etc/group");
};
- defer io::close(&file);
+ defer io::close(file);
for (true) {
- let ent = match (nextgr(&file)) {
+ let ent = match (nextgr(file)) {
case e: grent =>
yield e;
case io::EOF =>
diff --git a/unix/passwd/passwd.ha b/unix/passwd/passwd.ha
@@ -22,10 +22,10 @@ export type pwent = struct {
shell: str,
};
-// Reads a Unix-like password entry from a stream. The caller must free the
-// result using [[pwent_finish]].
-export fn nextpw(stream: *io::stream) (pwent | io::EOF | io::error | invalid) = {
- let line = match (bufio::scanline(stream)?) {
+// Reads a Unix-like password entry from an [[io::handle]]. The caller must free
+// the result using [[pwent_finish]].
+export fn nextpw(file: io::handle) (pwent | io::EOF | io::error | invalid) = {
+ let line = match (bufio::scanline(file)?) {
case io::EOF =>
return io::EOF;
case ln: []u8 =>
@@ -91,10 +91,10 @@ export fn getuser(username: str) (pwent | void) = {
case =>
abort("Can't open /etc/passwd");
};
- defer io::close(&file);
+ defer io::close(file);
for (true) {
- let ent = match (nextpw(&file)) {
+ let ent = match (nextpw(file)) {
case e: pwent =>
yield e;
case io::EOF =>
diff --git a/unix/poll/+linux.ha b/unix/poll/+linux.ha
@@ -1,6 +1,7 @@
use errors;
-use time;
+use io;
use rt;
+use time;
// Events bitfield for the events and revents field of [[pollfd]].
export type event = enum i16 {
@@ -13,8 +14,7 @@ export type event = enum i16 {
// A single file descriptor to be polled.
export type pollfd = struct {
- // XXX: TODO: Update me to io::file?
- fd: int,
+ fd: io::file,
events: event,
revents: event,
};
diff --git a/unix/resolvconf/load.ha b/unix/resolvconf/load.ha
@@ -23,10 +23,10 @@ export fn load() []ip::addr = {
};
const file = os::open(path)!;
- defer io::close(&file);
+ defer io::close(file);
for (true) {
- const line = match (bufio::scanline(&file)) {
+ const line = match (bufio::scanline(file)) {
case io::EOF =>
break;
case line: []u8 =>
diff --git a/unix/tty/+linux/isatty.ha b/unix/tty/+linux/isatty.ha
@@ -3,13 +3,7 @@ use io;
use os;
// Returns whether the given stream is connected to a terminal.
-export fn isatty(stream: *io::stream) bool = {
- let fd = match (io::unwrapfd(stream)) {
- case void =>
- return false;
- case fd: int =>
- yield fd;
- };
+export fn isatty(fd: io::file) bool = {
let wsz = rt::winsize { ... };
match (rt::ioctl(fd, rt::TIOCGWINSZ, &wsz: *void)) {
case e: rt::errno =>
diff --git a/unix/tty/+linux/winsize.ha b/unix/tty/+linux/winsize.ha
@@ -3,14 +3,8 @@ use io;
use os;
use rt;
-// Returns the dimensions of underlying terminal of the stream.
-export fn winsize(tty: *io::stream) (ttysize | error) = {
- let fd = match (io::unwrapfd(tty)) {
- case void =>
- return errors::unsupported;
- case fd: int =>
- yield fd;
- };
+// Returns the dimensions of underlying terminal for an [[io::file]].
+export fn winsize(fd: io::file) (ttysize | error) = {
let wsz = rt::winsize { ... };
match (rt::ioctl(fd, rt::TIOCGWINSZ, &wsz: *void)) {
case e: rt::errno =>
@@ -24,7 +18,7 @@ export fn winsize(tty: *io::stream) (ttysize | error) = {
};
case int =>
return ttysize {
- rows = wsz.ws_row,
+ rows = wsz.ws_row,
columns = wsz.ws_col,
};
};
diff --git a/uuid/uuid.ha b/uuid/uuid.ha
@@ -50,8 +50,8 @@ export fn generate() uuid = {
// Returns true if two UUIDs are equal.
export fn compare(a: uuid, b: uuid) bool = bytes::equal(a, b);
-// Encodes a UUID as a string and writes it to a stream.
-export fn encode(out: *io::stream, in: uuid) (size | io::error) = {
+// Encodes a UUID as a string and writes it to an I/O handle.
+export fn encode(out: io::handle, in: uuid) (size | io::error) = {
let z = 0z;
for (let i = TIME_LOW; i < TIME_LOW + 4; i += 1) {
z += fmt::fprintf(out, "{:02x}", in[i])?;
@@ -72,8 +72,8 @@ export fn encode(out: *io::stream, in: uuid) (size | io::error) = {
return z;
};
-// Encodes a UUID as a URI and writes it to a stream.
-export fn uri(out: *io::stream, in: uuid) (size | io::error) = {
+// Encodes a UUID as a URI and writes it to an I/O handle.
+export fn uri(out: io::handle, in: uuid) (size | io::error) = {
return fmt::fprintf(out, "urn:uuid:")? + encode(out, in)?;
};
@@ -105,8 +105,8 @@ export fn encodeuri(in: uuid) str = {
assert(encodestr(in) == "3ded910c-8080-4bc8-af39-b6cccee36741");
};
-// Decodes a UUID as a string from an [[io::stream]].
-export fn decode(in: *io::stream) (uuid | invalid | io::error) = {
+// Decodes a UUID as a string from an [[io::handle]].
+export fn decode(in: io::handle) (uuid | invalid | io::error) = {
let u: uuid = [0...];
for (let i = 0z; i < len(u); i += 1) {
let buf: [2]u8 = [0...];