io
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, handle, to specify the object of the I/O operation. This type is a tagged union of file and *stream. Most programmers should prefer to use handle unless they specifically require the special semantics of one of its subtypes.
The 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 network connection, and so on. The use of file is generally required when working with host I/O, such as with unix::poll::.
The 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 handle. Several standard library modules offer implementations of stream for one reason or another, such as bufio::. Additionally, the io module provides some useful general-purpose I/O streams, such as tee.
Index
Types
type EOF = done;
type closer = fn(s: *stream) (void | error);
type copier = fn(to: *stream, from: *stream) (size | error);
type dupflag = enum {
NONE = 0,
NOCLOEXEC = rt::FD_CLOEXEC,
};
type file = int;
type handle = (file | *stream);
type lockop = enum {
SHARED = rt::LOCK_SH, EXCLUSIVE = rt::LOCK_EX, UNLOCK = rt::LOCK_UN, };
type mflag = enum uint {
SHARED = rt::MAP_SHARED,
PRIVATE = rt::MAP_PRIVATE,
SHARED_VALIDATE = rt::MAP_SHARED_VALIDATE,
FIXED = rt::MAP_FIXED,
ANON = rt::MAP_ANON,
NORESERVE = rt::MAP_NORESERVE,
GROWSDOWN = rt::MAP_GROWSDOWN,
DENYWRITE = rt::MAP_DENYWRITE,
EXECUTABLE = rt::MAP_EXECUTABLE,
LOCKED = rt::MAP_LOCKED,
POPULATE = rt::MAP_POPULATE,
NONBLOCK = rt::MAP_NONBLOCK,
STACK = rt::MAP_STACK,
HUGETLB = rt::MAP_HUGETLB,
SYNC = rt::MAP_SYNC,
FIXED_NOREPLACE = rt::MAP_FIXED_NOREPLACE,
FILE = rt::MAP_FILE,
HUGE_SHIFT = rt::MAP_HUGE_SHIFT,
HUGE_MASK = rt::MAP_HUGE_MASK,
HUGE_64KB = rt::MAP_HUGE_64KB,
HUGE_512KB = rt::MAP_HUGE_512KB,
HUGE_1MB = rt::MAP_HUGE_1MB,
HUGE_2MB = rt::MAP_HUGE_2MB,
HUGE_8MB = rt::MAP_HUGE_8MB,
HUGE_16MB = rt::MAP_HUGE_16MB,
HUGE_32MB = rt::MAP_HUGE_32MB,
HUGE_256MB = rt::MAP_HUGE_256MB,
HUGE_512MB = rt::MAP_HUGE_512MB,
HUGE_1GB = rt::MAP_HUGE_1GB,
HUGE_2GB = rt::MAP_HUGE_2GB,
HUGE_16GB = rt::MAP_HUGE_16GB,
};
type mode = enum u8 {
NONE = 0,
READ = 1 << 0,
WRITE = 1 << 1,
RDWR = READ | WRITE,
};
type off = i64;
type prot = enum uint {
NONE = rt::PROT_NONE,
READ = rt::PROT_READ,
WRITE = rt::PROT_WRITE,
EXEC = rt::PROT_EXEC,
GROWSDOWN = rt::PROT_GROWSDOWN,
GROWSUP = rt::PROT_GROWSUP,
};
type reader = fn(s: *stream, buf: []u8) (size | EOF | error);
type seeker = fn(s: *stream, off: off, w: whence) (off | error);
type stream = *vtable;
type vtable = struct {
reader: nullable *reader,
writer: nullable *writer,
closer: nullable *closer,
seeker: nullable *seeker,
copier: nullable *copier,
};
type whence = enum {
SET = 0, CUR = 1, END = 2, };
type writer = fn(s: *stream, buf: const []u8) (size | error);
type limitstream = struct {
vtable: stream,
source: handle,
limit: size,
};
type teestream = struct {
vtable: stream,
h: handle,
sink: handle,
};
type vector = rt::iovec;
Errors
type error = !(...errors::error | underread);
type underread = !size;
Globals
const empty: *stream;
const zero: *stream;
Functions
fn close(h: handle) (void | error);
fn copy(dest: handle, src: handle) (size | error);
fn drain(in: handle) ([]u8 | error);
fn dup(old: file, flags: dupflag) (file | error);
fn dup2(old: file, new: file, flags: dupflag) (file | error);
fn fdatasync(fd: file) (void | error);
fn fdopen(fd: int) file;
fn fsync(fd: file) (void | error);
fn limitreader(source: handle, limit: size) limitstream;
fn limitwriter(source: handle, limit: size) limitstream;
fn lock(fd: file, block: bool, op: lockop) (bool | error);
fn mkvector(buf: []u8) vector;
fn mmap(addr: nullable *opaque, length: size, prot: prot, flags: mflag, fd: file, offs: size) (*opaque | errors::error);
fn munmap(addr: *opaque, length: size) (void | errors::error);
fn read(h: handle, buf: []u8) (size | EOF | error);
fn readall(in: handle, buf: []u8) (size | EOF | error);
fn readv(fd: file, vectors: vector...) (size | EOF | error);
fn seek(h: handle, off: off, w: whence) (off | error);
fn strerror(err: error) str;
fn tee(h: handle, sink: handle) teestream;
fn tell(h: handle) (off | error);
fn trunc(fd: file, ln: size) (void | error);
fn write(h: handle, buf: const []u8) (size | error);
fn writeall(out: handle, buf: const []u8) (size | error);
fn writev(fd: file, vectors: vector...) (size | error);
Types
type EOF
type EOF = done;
Indicates an end-of-file condition.
type closer
type closer = fn(s: *stream) (void | error);
The interface for a stream which can be closed. This function should close and free any underlying resources, and cannot be used again.
type copier
type copier = fn(to: *stream, from: *stream) (size | error);
The interface for a stream which has first-class support for copying data from another stream. Often this only works if the second stream is of the same underlying stream type. This is optional, copy still works even with a stream which does not implement this (it falls back to calling read and write in a loop).
Returns the number of bytes copied, or an error if one occured. Do not close either stream. If the operation is unsupported for this particular pair of streams, return errors::unsupported to have copy proceed with its fallback implementation.
type dupflag
type dupflag = enum {
NONE = 0,
NOCLOEXEC = rt::FD_CLOEXEC,
};
Flags for dup and dup2 operations.
type file
type file = int;
This is an opaque type which encloses an OS-level file handle resource. It can be used as a handle in most situations, but there are some APIs which require a file with some OS-level handle backing it - this type is used for such APIs.
On Linux, file is a file descriptor.
type handle
type handle = (file | *stream);
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.
type lockop
type lockop = enum {
SHARED = rt::LOCK_SH, EXCLUSIVE = rt::LOCK_EX, UNLOCK = rt::LOCK_UN, };
Lock operation to use with lock.
type mflag
type mflag = enum uint {
SHARED = rt::MAP_SHARED,
PRIVATE = rt::MAP_PRIVATE,
SHARED_VALIDATE = rt::MAP_SHARED_VALIDATE,
FIXED = rt::MAP_FIXED,
ANON = rt::MAP_ANON,
NORESERVE = rt::MAP_NORESERVE,
GROWSDOWN = rt::MAP_GROWSDOWN,
DENYWRITE = rt::MAP_DENYWRITE,
EXECUTABLE = rt::MAP_EXECUTABLE,
LOCKED = rt::MAP_LOCKED,
POPULATE = rt::MAP_POPULATE,
NONBLOCK = rt::MAP_NONBLOCK,
STACK = rt::MAP_STACK,
HUGETLB = rt::MAP_HUGETLB,
SYNC = rt::MAP_SYNC,
FIXED_NOREPLACE = rt::MAP_FIXED_NOREPLACE,
FILE = rt::MAP_FILE,
HUGE_SHIFT = rt::MAP_HUGE_SHIFT,
HUGE_MASK = rt::MAP_HUGE_MASK,
HUGE_64KB = rt::MAP_HUGE_64KB,
HUGE_512KB = rt::MAP_HUGE_512KB,
HUGE_1MB = rt::MAP_HUGE_1MB,
HUGE_2MB = rt::MAP_HUGE_2MB,
HUGE_8MB = rt::MAP_HUGE_8MB,
HUGE_16MB = rt::MAP_HUGE_16MB,
HUGE_32MB = rt::MAP_HUGE_32MB,
HUGE_256MB = rt::MAP_HUGE_256MB,
HUGE_512MB = rt::MAP_HUGE_512MB,
HUGE_1GB = rt::MAP_HUGE_1GB,
HUGE_2GB = rt::MAP_HUGE_2GB,
HUGE_16GB = rt::MAP_HUGE_16GB,
};
Values for the mmap flags parameter. Only the SHARED, PRIVATE, and FIXED values are portable.
type mode
type mode = enum u8 {
NONE = 0,
READ = 1 << 0,
WRITE = 1 << 1,
RDWR = READ | WRITE,
};
Used to indicate if a stream should be used for reading, or writing, or both.
type off
type off = i64;
An offset for a handle.
type prot
type prot = enum uint {
NONE = rt::PROT_NONE,
READ = rt::PROT_READ,
WRITE = rt::PROT_WRITE,
EXEC = rt::PROT_EXEC,
GROWSDOWN = rt::PROT_GROWSDOWN,
GROWSUP = rt::PROT_GROWSUP,
};
Values for the mmap prot parameter. Only the EXEC, READ, WRITE, and NONE values are portable.
type reader
type reader = fn(s: *stream, buf: []u8) (size | EOF | error);
The interface for a stream which can be read from. Reads up to len(buf) bytes from the reader into the given buffer, returning the number of bytes read or an error.
type seeker
type seeker = fn(s: *stream, off: off, w: whence) (off | error);
The interface for a stream which can be seeked. Sets the offset for the next read or write to offset, interpreted according to whence: whence::SET means relative to the start of the file, whence::CUR means relative to the current offset, and whence::END means relative to the end.
Returns the new offset relative to the start or an error.
type stream
type stream = *vtable;
A stream is a pointer to a vtable which allows for user-defined I/O abstractions to implement some subset of read, write, seek, close, and other I/O functionality in userspace. Embed to create a custom stream type:
export type my_stream = struct {
vtable: io::stream,
fd: int,
};
const my_vtable: io::vtable = io::vtable {
reader = &my_stream_read,
writer = &my_stream_write,
closer = null,
...
};
export fn open(path: str) my_stream = {
let fd = // ...
return my_stream {
vtable = &my_vtable,
fd = fd,
...
});
};
let stream = open("example");
io::read(&stream, buf)!;
type vtable
type vtable = struct {
reader: nullable *reader,
writer: nullable *writer,
closer: nullable *closer,
seeker: nullable *seeker,
copier: nullable *copier,
};
The vtable type defines a set of virtual functions for a stream.
type whence
type whence = enum {
SET = 0, CUR = 1, END = 2, };
From "whence" a seek operation should occur.
type writer
type writer = fn(s: *stream, buf: const []u8) (size | error);
The interface for a stream which can be written to. Writes up to len(buf) bytes to the writer from the given buffer, returning the number of bytes written or an error.
type limitstream
Show undocumented member
type limitstream = struct {
vtable: stream,
source: handle,
limit: size,
};
type teestream
Show undocumented member
type teestream = struct {
vtable: stream,
h: handle,
sink: handle,
};
type vector
Show undocumented member
type vector = rt::iovec;
Errors
type error
type error = !(...errors::error | underread);
Any error which may be returned from an I/O function.
type underread
type underread = !size;
Returned by readall if the I/O handle returned EOF prior to completely reading an item. Stores the amount that was succesfully read.
Globals
let empty
const empty: *stream;
A stream which always reads EOF and discards any writes.
let zero
const zero: *stream;
A stream which always reads zeroes and discards any writes.
Functions
fn close
fn close(h: handle) (void | error);
Closes a handle. No further operations against this handle are permitted after calling this function. Closing a file handle can fail only under certain conditions (for example, closing a file twice, or an interrupted syscall). However, the user should not attempt to close the file again on failure - at best the user should print a diagnostic message and move on. See close(2) for details.
fn copy
fn copy(dest: handle, src: handle) (size | error);
Copies data from one handle into another. If reading from the source file returns zero (underread), the copy is terminated and the amount of data copied is returned. Note that this function will never return if the source handle is infinite.
fn drain
fn drain(in: handle) ([]u8 | error);
Reads an entire stream into a []u8. The caller must free the return value. Note that this function will never return if the handle is infinite.
fn dup
fn dup(old: file, flags: dupflag) (file | error);
Duplicates a file descriptor.
fn dup2
fn dup2(old: file, new: file, flags: dupflag) (file | error);
Duplicates a file descriptor and stores the new file at a specific file descriptor number. If the file indicated by "new" already refers to an open file, this file will be closed before the file descriptor is reused.
fn fdatasync
fn fdatasync(fd: file) (void | error);
Flushes all in-flight I/O for the given file descriptor to persistent storage, flushing any writes the kernel has cached. Only persists changes to the associated inode if it is not required for the file contents to be successfully retrieved. If the file size has changed, for example, this will block until the inode is updated; but it would not block on an update to its mtime.
fn fdopen
fn fdopen(fd: int) file;
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.
fn fsync
fn fsync(fd: file) (void | error);
Flushes all in-flight I/O for the given file descriptor to persistent storage, flushing any writes the kernel has cached, and any changes to the corresponding inode.
fn limitreader
fn limitreader(source: handle, limit: size) limitstream;
Create an overlay stream that only allows a limited amount of bytes to be read from the underlying stream. This stream does not need to be closed, and closing it does not close the underlying stream. Reading any data beyond the given limit causes the reader to return EOF.
fn limitwriter
fn limitwriter(source: handle, limit: size) limitstream;
Create an overlay stream that only allows a limited amount of bytes to be written to the underlying stream. This stream does not need to be closed, and closing it does not close the underlying stream. Writing beyond the given limit causes the writer to return short writes (as few as zero bytes).
fn lock
fn lock(fd: file, block: bool, op: lockop) (bool | error);
Apply or remove an advisory lock on an open file. If block is true, the request will block while waiting for the lock.
fn mkvector
fn mkvector(buf: []u8) vector;
Creates a vector for use with writev and readv.
fn mmap
fn mmap(addr: nullable *opaque, length: size, prot: prot, flags: mflag, fd: file, offs: size) (*opaque | errors::error);
Performs the mmap syscall. Consult your system for documentation on this function.
fn munmap
fn munmap(addr: *opaque, length: size) (void | errors::error);
Unmaps memory previously mapped with mmap.
fn read
fn read(h: handle, buf: []u8) (size | EOF | error);
Reads up to len(buf) bytes from a handle into the given buffer, returning the number of bytes read.
fn readall
fn readall(in: handle, buf: []u8) (size | EOF | error);
Reads an entire buffer, perhaps issuing several read calls to do so. If EOF is immediately encountered, it is returned; if EOF is encountered partway through reading the buffer, underread is returned.
fn readv
fn readv(fd: file, vectors: vector...) (size | EOF | error);
Performs a vectored read on the given file. A read is performed on each of the vectors, prepared with mkvector, in order, and the total number of bytes read is returned.
fn seek
fn seek(h: handle, off: off, w: whence) (off | error);
Sets the offset within a handle, returning the new offset. The new offset is obtained by adding off to the position specified by whence.
fn strerror
fn strerror(err: error) str;
Converts an I/O error into a user-friendly string.
fn tee
fn tee(h: handle, sink: handle) teestream;
Creates a stream which copies writes and reads into 'sink' after forwarding them to the handle 'h'. This stream does not need to be closed, and closing it will not close the secondary stream.
fn tell
fn tell(h: handle) (off | error);
Returns the current offset within a handle.
fn trunc
fn trunc(fd: file, ln: size) (void | error);
Truncate a file to a specified length. The file must be open for writing.
fn write
fn write(h: handle, buf: const []u8) (size | error);
Writes up to len(buf) bytes to the handle from the given buffer, returning the number of bytes written.
fn writeall
fn writeall(out: handle, buf: const []u8) (size | error);
Writes an entire buffer, perhaps issuing several write calls to do so. Aborts on errors after partial writes. Hence it should only be used if it is certain that the underlying writes will not fail after an initial write.
fn writev
fn writev(fd: file, vectors: vector...) (size | error);
Performs a vectored write on the given file. Each of the vectors, prepared with mkvector, are written to the file in order, and the total number of bytes written is returned.