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:
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