hare

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

commit 098879d7cd61043130abc1125ec63a1b564e9e31
parent 31b5c8a01444a2db589a30b0ed81ffb37b547a4c
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat,  4 Sep 2021 10:48:17 +0200

Remove os::streamfd, update net::*, unix::*

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mio/+linux/file.ha | 12++++++++++--
Mnet/+linux.ha | 8++++----
Mnet/dial/dial.ha | 2+-
Mnet/dial/ip.ha | 9+++++----
Mnet/dial/registry.ha | 2+-
Mnet/dns/query.ha | 21+++++++++++----------
Mnet/errors.ha | 2+-
Mnet/listener.ha | 4++--
Mnet/tcp/+linux.ha | 13+++++--------
Mnet/tcp/listener.ha | 2+-
Mnet/udp/+linux.ha | 52+++++++++++++---------------------------------------
Mnet/unix/+linux.ha | 6+++---
Mnet/unix/dial.ha | 4++--
Mnet/unix/listener.ha | 2+-
Dos/+linux/fdstream.ha | 122-------------------------------------------------------------------------------
Mos/+linux/stdfd.ha | 6+++---
Mscripts/gen-stdlib | 1-
Mstdlib.mk | 2--
Munix/tty/+linux/isatty.ha | 5+----
Munix/tty/+linux/winsize.ha | 5+----
20 files changed, 65 insertions(+), 215 deletions(-)

diff --git a/io/+linux/file.ha b/io/+linux/file.ha @@ -48,9 +48,17 @@ export fn is_file(s: *stream) bool = { || s.copier == &fd_copy; }; -// Returns the file descriptor for a given [[file]]. This function is not +// Returns the file descriptor for a given [[file]] or [[stream]]. In the latter +// case, a non-file input will cause the program to abort. This function is not // portable. -export fn fd(f: *file) int = f.fd; +export fn fd(f: (*file | *stream)) int = match (f) { + f: *file => f.fd, + f: *stream => { + assert(is_file(f)); + let f = f: *file; + yield f.fd; + }, +}; fn fd_read(s: *stream, buf: []u8) (size | EOF | error) = { let stream = s: *file; diff --git a/net/+linux.ha b/net/+linux.ha @@ -19,7 +19,7 @@ export fn listenerfd(l: *listener) (int | void) = { }; }; -export fn stream_accept(l: *listener) (*io::stream | error) = { +export fn stream_accept(l: *listener) (io::file | error) = { assert(l.accept == &stream_accept); let l = l: *stream_listener; let sn = rt::sockaddr {...}; @@ -30,9 +30,9 @@ export fn stream_accept(l: *listener) (*io::stream | error) = { }; static let namebuf: [32]u8 = [0...]; - return alloc(os::fdopen(fd, - fmt::bsprintf(namebuf, "<net connection {}>", fd), - io::mode::READ | io::mode::WRITE)); + return io::fdopen(fd, + fmt::bsprintf(namebuf, "<network fd {}>", fd), + io::mode::READ | io::mode::WRITE); }; export fn stream_shutdown(l: *listener) void = { diff --git a/net/dial/dial.ha b/net/dial/dial.ha @@ -33,7 +33,7 @@ export fn dial( proto: str, address: str, service: str, -) (*io::stream | error) = { +) (io::file | error) = { for (let i = 0z; i < len(default_protocols); i += 1) { const p = default_protocols[i]; if (p.name == proto) { diff --git a/net/dial/ip.ha b/net/dial/ip.ha @@ -3,14 +3,15 @@ use net; use net::ip; use net::tcp; use net::udp; +use io; -fn dial_tcp(addr: str, service: str) (*io::stream | error) = { +fn dial_tcp(addr: str, service: str) (io::file | error) = { const result = resolve("tcp", addr, service)?; const addrs = result.0, port = result.1; for (let i = 0z; i < len(addrs); i += 1) { const addr = addrs[i]; match (tcp::connect(addr, port)) { - conn: *io::stream => return conn, + conn: io::file => return conn, err: net::error => if (i + 1 >= len(addrs)) { return err; }, @@ -19,13 +20,13 @@ fn dial_tcp(addr: str, service: str) (*io::stream | error) = { abort(); // Unreachable }; -fn dial_udp(addr: str, service: str) (*io::stream | error) = { +fn dial_udp(addr: str, service: str) (io::file | error) = { const result = resolve("udp", addr, service)?; const addrs = result.0, port = result.1; for (let i = 0z; i < len(addrs); i += 1) { const addr = addrs[i]; match (udp::connect(addr, port)) { - sock: udp::socket => return udp::stream(sock), + sock: io::file => return sock, err: net::error => if (i + 1 >= len(addrs)) { return err; }, diff --git a/net/dial/registry.ha b/net/dial/registry.ha @@ -26,7 +26,7 @@ export fn strerror(err: error) const str = { }; // A dialer is a function which implements dial for a specific protocol. -export type dialer = fn(addr: str, service: str) (*io::stream | error); +export type dialer = fn(addr: str, service: str) (io::file | error); type protocol = struct { name: str, diff --git a/net/dns/query.ha b/net/dns/query.ha @@ -1,4 +1,5 @@ use errors; +use io; use net::ip; use net::udp; use time; @@ -25,18 +26,18 @@ export fn query(query: *message, servers: ip::addr...) (*message | error) = { ]; }; - const socket4 = udp::listen(ip::ANY_V4, 0)?; - defer udp::close(socket4); - const socket6 = udp::listen(ip::ANY_V6, 0)?; - defer udp::close(socket6); + let socket4 = udp::listen(ip::ANY_V4, 0)?; + defer io::close(&socket4); + let socket6 = udp::listen(ip::ANY_V6, 0)?; + defer io::close(&socket6); const pollfd: [_]poll::pollfd = [ poll::pollfd { - fd = udp::sockfd(socket4), + fd = io::fd(&socket4), events = poll::event::POLLIN, ... }, poll::pollfd { - fd = udp::sockfd(socket6), + fd = io::fd(&socket6), events = poll::event::POLLIN, ... }, @@ -49,10 +50,10 @@ 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]) { ip::addr4 => { - udp::sendto(socket4, sendbuf[..z], servers[i], 53)?; + udp::sendto(&socket4, sendbuf[..z], servers[i], 53)?; }, ip::addr6 => { - udp::sendto(socket6, sendbuf[..z], servers[i], 53)?; + udp::sendto(&socket6, sendbuf[..z], servers[i], 53)?; }, }; @@ -66,10 +67,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/errors.ha b/net/errors.ha @@ -25,6 +25,6 @@ export fn strerror(err: error) const str = { // need to use this interface directly, preferring functions such as // [[net::tcp::accept]]. export type listener = struct { - accept: nullable *fn(l: *listener) (*io::stream | error), + accept: nullable *fn(l: *listener) (io::file | error), shutdown: nullable *fn(l: *listener) void, }; diff --git a/net/listener.ha b/net/listener.ha @@ -3,9 +3,9 @@ use io; // Accepts the next connection from a listener. Blocks until a new connection is // available. -export fn accept(l: *listener) (*io::stream | error) = { +export fn accept(l: *listener) (io::file | error) = { return match (l.accept) { - f: *fn(l: *listener) (*io::stream | error) => f(l), + f: *fn(l: *listener) (io::file | error) => f(l), null => errors::unsupported, }; }; diff --git a/net/tcp/+linux.ha b/net/tcp/+linux.ha @@ -11,7 +11,7 @@ export fn connect( addr: ip::addr, port: u16, options: connect_option... -) (*io::stream | net::error) = { +) (io::file | net::error) = { const sockaddr = ip::to_native(addr, port); const sockfd = match (rt::socket(match (addr) { ip::addr4 => rt::AF_INET: int, @@ -32,8 +32,8 @@ export fn connect( err: rt::errno => return errors::errno(err), int => void, }; - return alloc(os::fdopen(sockfd, ip::string(addr), - io::mode::READ | io::mode::WRITE)); + return io::fdopen(sockfd, ip::string(addr), + io::mode::READ | io::mode::WRITE); }; // Binds a TCP listener to the given address. @@ -99,11 +99,8 @@ export fn listen( // Returns the remote address for a given connection, or void if none is // available. -export fn peeraddr(stream: *io::stream) ((ip::addr, u16) | void) = { - let fd = match (os::streamfd(stream, true)) { - fd: int => fd, - void => return, - }; +export fn peeraddr(peer: *io::file) ((ip::addr, u16) | void) = { + let fd = io::fd(peer); let sn = rt::sockaddr {...}; let sz = size(rt::sockaddr): u32; if (rt::getpeername(fd, &sn, &sz) is rt::errno) { diff --git a/net/tcp/listener.ha b/net/tcp/listener.ha @@ -3,7 +3,7 @@ use net; // Accepts the next connection from a listener. Blocks until a new connection is // available. This is a convenience wrapper around [[net::accept]]. -export fn accept(l: *net::listener) (*io::stream | net::error) = { +export fn accept(l: *net::listener) (io::file | net::error) = { return net::accept(l); }; diff --git a/net/udp/+linux.ha b/net/udp/+linux.ha @@ -5,13 +5,11 @@ use net; use os; use rt; -export type socket = int; - // Creates a UDP socket and sets the default destination to the given address. export fn connect( dest: ip::addr, port: u16, -) (socket | net::error) = { +) (io::file | net::error) = { const sockfd = match (rt::socket(match (dest) { ip::addr4 => rt::AF_INET: int, ip::addr6 => rt::AF_INET6: int, @@ -23,39 +21,19 @@ export fn connect( const sockaddr = ip::to_native(dest, port); const sz = size(rt::sockaddr): u32; return match (rt::connect(sockfd, &sockaddr, sz)) { - int => sockfd, + int => io::fdopen(sockfd, + "<udp connection>", + io::mode::READ | io::mode::WRITE), err: rt::errno => errors::errno(err), }; }; -// Gets the file descriptor associated with a UDP socket. This function is -// non-portable. -export fn sockfd(sock: socket) int = sock; - -// Creates an [[io::stream]] for an open UDP socket, which allows you to send -// and receive UDP packets using [[io::read]] and [[io::write]], albiet without -// being able to receive the sender address. Only meaningful if the socket was -// created with [[connect]]. -export fn stream(sock: socket) *io::stream = { - return alloc(os::fdopen(sock, "<UDP stream>", - io::mode::READ | io::mode::WRITE)); -}; - -// Obtains a [[socket]] for a given [[io::stream]], or returns void if it is not -// a socket stream. -export fn socket_for(stream: *io::stream) (socket | void) = { - return match (os::streamfd(stream, false)) { - fd: int => fd: socket, - void => void, - }; -}; - // Creates a UDP socket bound to an interface. export fn listen( addr: ip::addr, port: u16, options: listen_option... -) (socket | net::error) = { +) (io::file | net::error) = { const sockfd = match (rt::socket(match (addr) { ip::addr4 => rt::AF_INET: int, ip::addr6 => rt::AF_INET6: int, @@ -85,17 +63,13 @@ export fn listen( *options[i] = addr.1; }; - return sockfd; -}; - -// Closes a UDP socket. -export fn close(sock: socket) void = { - rt::close(sock): void; + return io::fdopen(sockfd, "<udp listener>", + io::mode::READ | io::mode::WRITE); }; // Sends a UDP packet to the destination previously specified by [[connect]]. -export fn send(sock: socket, buf: []u8) (size | net::error) = { - return match (rt::send(sock, buf: *[*]u8, len(buf), 0)) { +export fn send(sock: *io::file, buf: []u8) (size | net::error) = { + return match (rt::send(io::fd(sock), buf: *[*]u8, len(buf), 0)) { sz: size => sz, err: rt::errno => errors::errno(err), }; @@ -103,14 +77,14 @@ export fn send(sock: socket, buf: []u8) (size | net::error) = { // Sends a UDP packet using this socket. export fn sendto( - sock: socket, + 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; - return match (rt::sendto(sock, buf: *[*]u8, len(buf), + return match (rt::sendto(io::fd(sock), buf: *[*]u8, len(buf), 0, &sockaddr, sz)) { sz: size => sz, err: rt::errno => errors::errno(err), @@ -119,14 +93,14 @@ export fn sendto( // Receives a UDP packet from a bound socket. export fn recvfrom( - sock: socket, + 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(sock, buf: *[*]u8, len(buf), + const sz = match (rt::recvfrom(io::fd(sock), buf: *[*]u8, len(buf), 0, &sockaddr, &addrsz)) { sz: size => sz, err: rt::errno => return errors::errno(err), diff --git a/net/unix/+linux.ha b/net/unix/+linux.ha @@ -9,7 +9,7 @@ use types; // Opens a UNIX socket connection to the path. Blocks until the connection is // established. -export fn connect(addr: addr) (*io::stream | net::error) = { +export fn connect(addr: addr) (io::file | net::error) = { let sockaddr = match (to_native(addr)) { a: rt::sockaddr => a, invalid => return errors::unsupported, // path too long @@ -27,9 +27,9 @@ export fn connect(addr: addr) (*io::stream | net::error) = { int => void, }; static let buf: [rt::UNIX_PATH_MAX + 32]u8 = [0...]; - return alloc(os::fdopen(sockfd, + return io::fdopen(sockfd, fmt::bsprintf(buf, "<unix connection {}>", addr), - io::mode::READ | io::mode::WRITE)); + io::mode::READ | io::mode::WRITE); }; // Binds a UNIX socket listener to the given path. diff --git a/net/unix/dial.ha b/net/unix/dial.ha @@ -2,9 +2,9 @@ use io; use net::dial; use net; -fn dial_unix(addr: str, service: str) (*io::stream | dial::error) = { +fn dial_unix(addr: str, service: str) (io::file | dial::error) = { return match (connect(addr)) { - conn: *io::stream => conn, + conn: io::file => conn, err: net::error => err, }; }; diff --git a/net/unix/listener.ha b/net/unix/listener.ha @@ -3,7 +3,7 @@ use net; // Accepts the next connection from a listener. Blocks until a new connection is // available. This is a convenience wrapper around [[net::accept]]. -export fn accept(l: *net::listener) (*io::stream | net::error) = { +export fn accept(l: *net::listener) (io::file | net::error) = { return net::accept(l); }; diff --git a/os/+linux/fdstream.ha b/os/+linux/fdstream.ha @@ -1,122 +0,0 @@ -use errors; -use io; -use rt; - -export type fdstream = struct { - io::stream, - fd: int, -}; - -// Opens a Unix file descriptor as an io::stream. -export fn fdopen(fd: int, name: str, mode: io::mode) fdstream = { - let stream = fdstream { - name = name, - closer = &fd_close, - copier = &fd_copy, - seeker = &fd_seek, - fd = fd, - ... - }; - if (mode & io::mode::READ == io::mode::READ) { - stream.reader = &fd_read; - }; - if (mode & io::mode::WRITE == io::mode::WRITE) { - stream.writer = &fd_write; - }; - return stream; -}; - -fn is_fdstream(s: *io::stream) bool = { - return s.reader == &fd_read - || s.writer == &fd_write - || s.closer == &fd_close - || s.copier == &fd_copy; -}; - -// Returns the file descriptor for a given [[io::stream]]. If there is no fd -// associated with this stream, void is returned. -// -// If 'unwrap' is true, the stream will be unwrapped until an fdstream is found. -// Note that many stream wrappers (such as [[io::tee]]) provide important -// functionality, and using the file descriptor directly will circumvent that -// functionality. Use with caution. -export fn streamfd(s: *io::stream, unwrap: bool) (int | void) = { - for (unwrap && !is_fdstream(s)) { - s = match (io::source(s)) { - errors::unsupported => return, - s: *io::stream => s, - }; - }; - if (!is_fdstream(s)) { - return; - }; - let stream = s: *fdstream; - return stream.fd; -}; - -fn fd_read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = { - let stream = s: *fdstream; - return match (rt::read(stream.fd, buf: *[*]u8, len(buf))) { - err: rt::errno => errors::errno(err), - n: size => switch (n) { - 0 => io::EOF, - * => n, - }, - }; -}; - -fn fd_write(s: *io::stream, buf: const []u8) (size | io::error) = { - let stream = s: *fdstream; - return match (rt::write(stream.fd, buf: *const [*]u8, len(buf))) { - err: rt::errno => errors::errno(err), - n: size => n, - }; -}; - -fn fd_close(s: *io::stream) void = { - let stream = s: *fdstream; - rt::close(stream.fd)!; -}; - -def SENDFILE_MAX: size = 2147479552z; - -fn fd_copy(to: *io::stream, from: *io::stream) (size | io::error) = { - if (!is_fdstream(from)) { - return errors::unsupported; - }; - - let to = to: *fdstream, from = from: *fdstream; - let sum = 0z; - for (true) { - let n = match (rt::sendfile(to.fd, from.fd, - null, SENDFILE_MAX)) { - err: rt::errno => switch (err) { - rt::EINVAL => { - if (sum == 0) { - return errors::unsupported; - }; - return errors::errno(err); - }, - * => return errors::errno(err), - }, - n: size => switch (n) { - 0 => return sum, - * => n, - }, - }; - sum += n; - }; - return sum; -}; - -fn fd_seek( - s: *io::stream, - off: io::off, - whence: io::whence, -) (io::off | io::error) = { - let stream = s: *fdstream; - return match (rt::lseek(stream.fd, off: i64, whence: uint)) { - err: rt::errno => errors::errno(err), - n: i64 => n: io::off, - }; -}; diff --git a/os/+linux/stdfd.ha b/os/+linux/stdfd.ha @@ -1,9 +1,9 @@ use bufio; use io; -let static_stdin_fd: io::file = fdstream { ... }; -let static_stdout_fd: io::file = fdstream { ... }; -let static_stderr_fd: io::file = fdstream { ... }; +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 { ... }; let static_stdout_bufio: bufio::bufstream = bufio::bufstream { ... }; diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -666,7 +666,6 @@ os() { '$(PLATFORM)/environ.ha' \ '$(PLATFORM)/exit.ha' \ '$(PLATFORM)/dirfdfs.ha' \ - '$(PLATFORM)/fdstream.ha' \ '$(PLATFORM)/stdfd.ha' \ '$(PLATFORM)/fs.ha' \ stdfd.ha \ diff --git a/stdlib.mk b/stdlib.mk @@ -977,7 +977,6 @@ stdlib_os_srcs= \ $(STDLIB)/os/$(PLATFORM)/environ.ha \ $(STDLIB)/os/$(PLATFORM)/exit.ha \ $(STDLIB)/os/$(PLATFORM)/dirfdfs.ha \ - $(STDLIB)/os/$(PLATFORM)/fdstream.ha \ $(STDLIB)/os/$(PLATFORM)/stdfd.ha \ $(STDLIB)/os/$(PLATFORM)/fs.ha \ $(STDLIB)/os/stdfd.ha \ @@ -2213,7 +2212,6 @@ testlib_os_srcs= \ $(STDLIB)/os/$(PLATFORM)/environ.ha \ $(STDLIB)/os/$(PLATFORM)/exit.ha \ $(STDLIB)/os/$(PLATFORM)/dirfdfs.ha \ - $(STDLIB)/os/$(PLATFORM)/fdstream.ha \ $(STDLIB)/os/$(PLATFORM)/stdfd.ha \ $(STDLIB)/os/$(PLATFORM)/fs.ha \ $(STDLIB)/os/stdfd.ha \ diff --git a/unix/tty/+linux/isatty.ha b/unix/tty/+linux/isatty.ha @@ -4,10 +4,7 @@ use os; // Returns whether the given stream is connected to a terminal. export fn isatty(stream: *io::stream) bool = { - let fd = match (os::streamfd(stream, true)) { - f: int => f, - void => return false, - }; + let fd = io::fd(stream); let wsz = rt::winsize { ... }; return match (rt::ioctl(fd, rt::TIOCGWINSZ, &wsz: *void)) { e: rt::errno => false, diff --git a/unix/tty/+linux/winsize.ha b/unix/tty/+linux/winsize.ha @@ -5,10 +5,7 @@ use rt; // Returns the dimensions of underlying terminal of the stream. export fn winsize(tty: *io::stream) (ttysize | error) = { - let fd = match (os::streamfd(tty, true)) { - f: int => f, - void => return errors::invalid, - }; + let fd = io::fd(tty); let wsz = rt::winsize { ... }; return match (rt::ioctl(fd, rt::TIOCGWINSZ, &wsz: *void)) { e: rt::errno => switch (e: int) {