io+x86_64 +linux

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,
	// Causes [[dup]] and [[dup2]] not to set the CLOEXEC flag on the
	// duplicated file descriptor. By default, CLOEXEC is set.
	NOCLOEXEC = rt::FD_CLOEXEC,
};
type file = int;
type handle = (file | *stream);
type lockop = enum {
	SHARED = rt::LOCK_SH, // shared file lock
	EXCLUSIVE = rt::LOCK_EX, // exclusive file lock
	UNLOCK = rt::LOCK_UN, // unlock file
};
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, // Relative to beginning (i.e. set absolute position).
	CUR = 1, // Relative to current position.
	END = 2, // Relative to end of handle.
};
type writer = fn(s: *stream, buf: const []u8) (size | error);

// Undocumented types:
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 fdopen(fd: int) file;
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[link]

type EOF = done;

Indicates an end-of-file condition.

type closer[link]

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[link]

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[link]

type dupflag = enum {
	NONE = 0,
	// Causes [[dup]] and [[dup2]] not to set the CLOEXEC flag on the
	// duplicated file descriptor. By default, CLOEXEC is set.
	NOCLOEXEC = rt::FD_CLOEXEC,
};

Flags for dup and dup2 operations.

type file[link]

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[link]

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[link]

type lockop = enum {
	SHARED = rt::LOCK_SH, // shared file lock
	EXCLUSIVE = rt::LOCK_EX, // exclusive file lock
	UNLOCK = rt::LOCK_UN, // unlock file
};

Lock operation to use with lock.

type mflag[link]

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[link]

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[link]

type off = i64;

An offset for a handle.

type prot[link]

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[link]

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[link]

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[link]

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[link]

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[link]

type whence = enum {
	SET = 0, // Relative to beginning (i.e. set absolute position).
	CUR = 1, // Relative to current position.
	END = 2, // Relative to end of handle.
};

From "whence" a seek operation should occur.

type writer[link]

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[link]

Show undocumented member
type limitstream = struct {
	vtable: stream,
	source: handle,
	limit: size,
};

type teestream[link]

Show undocumented member
type teestream = struct {
	vtable: stream,
	h: handle,
	sink: handle,
};

type vector[link]

Show undocumented member
type vector = rt::iovec;

Errors

type error[link]

type error = !(...errors::error | underread);

Any error which may be returned from an I/O function.

type underread[link]

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[link]

const empty: *stream;

A stream which always reads EOF and discards any writes.

let zero[link]

const zero: *stream;

A stream which always reads zeroes and discards any writes.

Functions

fn close[link]

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[link]

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[link]

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[link]

fn dup(old: file, flags: dupflag) (file | error);

Duplicates a file descriptor.

fn dup2[link]

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 fdopen[link]

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 limitreader[link]

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[link]

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[link]

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[link]

fn mkvector(buf: []u8) vector;

Creates a vector for use with writev and readv.

fn mmap[link]

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[link]

fn munmap(addr: *opaque, length: size) (void | errors::error);

Unmaps memory previously mapped with mmap.

fn read[link]

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[link]

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[link]

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[link]

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[link]

fn strerror(err: error) str;

Converts an I/O error into a user-friendly string.

fn tee[link]

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[link]

fn tell(h: handle) (off | error);

Returns the current offset within a handle.

fn trunc[link]

fn trunc(fd: file, ln: size) (void | error);

Truncate a file to a specified length. The file must be open for writing.

fn write[link]

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[link]

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[link]

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.