hare

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

commit 7ba16d2f5ee8cddb16e9a52c16e4b0bc4c2b6d78
parent 780129f378121fbad5d03fedd354d53513738b24
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat,  4 Sep 2021 10:15:09 +0200

io: add io::file

This is copied and edited from os::fdstream, which will be removed in a
future commit.

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

Diffstat:
Aio/+linux/file.ha | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mos/+linux/fdstream.ha | 1-
Mscripts/gen-stdlib | 1+
Mstdlib.mk | 4+++-
4 files changed, 115 insertions(+), 2 deletions(-)

diff --git a/io/+linux/file.ha b/io/+linux/file.ha @@ -0,0 +1,111 @@ +use errors; +use rt; + +// This is an opaque type which encloses an OS-level file handle resource (on +// Unix, a file descriptor, or "fd") within a stream. It can be used as an +// [[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, +}; + +// 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, + 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; +}; + +export fn is_file(s: *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 [[file]]. This function is not +// portable. +export fn fd(f: *file) int = f.fd; + +fn fd_read(s: *stream, buf: []u8) (size | EOF | error) = { + let stream = s: *file; + return match (rt::read(stream.fd, buf: *[*]u8, len(buf))) { + err: rt::errno => errors::errno(err), + n: size => switch (n) { + 0 => EOF, + * => n, + }, + }; +}; + +fn fd_write(s: *stream, buf: const []u8) (size | error) = { + let stream = s: *file; + return match (rt::write(stream.fd, buf: *const [*]u8, len(buf))) { + err: rt::errno => errors::errno(err), + n: size => n, + }; +}; + +fn fd_close(s: *stream) void = { + let stream = s: *file; + rt::close(stream.fd)!; +}; + +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; + 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: *stream, + off: off, + whence: whence, +) (off | error) = { + let stream = s: *file; + return match (rt::lseek(stream.fd, off: i64, whence: uint)) { + err: rt::errno => errors::errno(err), + n: i64 => n: off, + }; +}; diff --git a/os/+linux/fdstream.ha b/os/+linux/fdstream.ha @@ -1,7 +1,6 @@ use errors; use io; use rt; -use strings; export type fdstream = struct { io::stream, diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -523,6 +523,7 @@ gensrcs_io() { stream.ha \ tee.ha \ types.ha \ + '$(PLATFORM)/file.ha' \ $* } diff --git a/stdlib.mk b/stdlib.mk @@ -798,7 +798,8 @@ stdlib_io_srcs= \ $(STDLIB)/io/println$(PLATFORM).ha \ $(STDLIB)/io/stream.ha \ $(STDLIB)/io/tee.ha \ - $(STDLIB)/io/types.ha + $(STDLIB)/io/types.ha \ + $(STDLIB)/io/$(PLATFORM)/file.ha $(HARECACHE)/io/io.ssa: $(stdlib_io_srcs) $(stdlib_rt) $(stdlib_strings) $(stdlib_errors) @printf 'HAREC \t$@\n' @@ -2030,6 +2031,7 @@ testlib_io_srcs= \ $(STDLIB)/io/stream.ha \ $(STDLIB)/io/tee.ha \ $(STDLIB)/io/types.ha \ + $(STDLIB)/io/$(PLATFORM)/file.ha \ $(STDLIB)/io/+test/copy.ha \ $(STDLIB)/io/+test/limit.ha \ $(STDLIB)/io/+test/stream.ha