WASI I/O is an I/O abstraction API which is currently focused on providing stream types.
In the future, the component model is expected to add built-in stream types; when it does, they are expected to subsume this API.
Types
use resource errorsince 0.2.0
from wasi:io/error@0.2.6
A resource which represents some error information.
The only method provided by this resource is to-debug-string,
which provides some human-readable information about the error.
In the wasi:io package, this resource is returned through the
wasi:io/streams/stream-error type.
To provide more specific error information, other interfaces may
offer functions to "downcast" this error into more specific types. For example,
errors returned from streams derived from filesystem types can be described using
the filesystem's own error-code type. This is done using the function
wasi:filesystem/types/filesystem-error-code, which takes a borrow<error>
parameter and returns an option<wasi:filesystem/types/error-code>.
The set of functions which can "downcast" an error into a more
concrete type is open.
to-debug-string: func() -> stringsince 0.2.0Returns a string that is suitable to assist humans in debugging this error.
WARNING: The returned string should not be consumed mechanically! It may change across platforms, hosts, or other implementation details. Parsing this string is a major platform-compatibility hazard.
use resource pollablesince 0.2.0
from wasi:io/poll@0.2.6
pollable represents a single I/O event which may be ready, or not.
ready: func() -> boolsince 0.2.0Return the readiness of a pollable. This function never blocks.
Returns
truewhen the pollable is ready, andfalseotherwise.block: func()since 0.2.0blockreturns immediately if the pollable is ready, and otherwise blocks until ready.This function is equivalent to calling
poll.pollon a list containing only this pollable.
variant stream-errorsince 0.2.0
An error for input-stream and output-stream operations.
last-operation-failed:errorThe last operation (a write or flush) failed before completion.
More information is available in the
errorpayload.After this, the stream will be closed. All future operations return
stream-error::closed.closedThe stream is closed: no more input will be accepted by the stream. A closed output-stream will return this error on all future operations.
resource input-streamsince 0.2.0
An input bytestream.
input-streams are non-blocking to the extent practical on underlying
platforms. I/O operations always return promptly; if fewer bytes are
promptly available than requested, they return the number of bytes promptly
available, which could even be zero. To wait for data to be available,
use the subscribe function to obtain a pollable which can be polled
for using wasi:io/poll.
read: func(len: u64) -> result<list<u8>, stream-error>since 0.2.0Perform a non-blocking read from the stream.
When the source of a
readis binary data, the bytes from the source are returned verbatim. When the source of areadis known to the implementation to be text, bytes containing the UTF-8 encoding of the text are returned.This function returns a list of bytes containing the read data, when successful. The returned list will contain up to
lenbytes; it may return fewer than requested, but not more. The list is empty when no bytes are available for reading at this time. The pollable given bysubscribewill be ready when more bytes are available.This function fails with a
stream-errorwhen the operation encounters an error, givinglast-operation-failed, or when the stream is closed, givingclosed.When the caller gives a
lenof 0, it represents a request to read 0 bytes. If the stream is still open, this call should succeed and return an empty list, or otherwise fail withclosed.The
lenparameter is au64, which could represent a list of u8 which is not possible to allocate in wasm32, or not desirable to allocate as as a return value by the callee. The callee may return a list of bytes less thanlenin size while more bytes are available for reading.blocking-read: func(len: u64) -> result<list<u8>, stream-error>since 0.2.0Read bytes from a stream, after blocking until at least one byte can be read. Except for blocking, behavior is identical to
read.skip: func(len: u64) -> result<u64, stream-error>since 0.2.0Skip bytes from a stream. Returns number of bytes skipped.
Behaves identical to
read, except instead of returning a list of bytes, returns the number of bytes consumed from the stream.blocking-skip: func(len: u64) -> result<u64, stream-error>since 0.2.0Skip bytes from a stream, after blocking until at least one byte can be skipped. Except for blocking behavior, identical to
skip.subscribe: func() -> pollablesince 0.2.0Create a
pollablewhich will resolve once either the specified stream has bytes available to read or the other end of the stream has been closed. The createdpollableis a child resource of theinput-stream. Implementations may trap if theinput-streamis dropped before all derivedpollables created with this function are dropped.
resource output-streamsince 0.2.0
An output bytestream.
output-streams are non-blocking to the extent practical on
underlying platforms. Except where specified otherwise, I/O operations also
always return promptly, after the number of bytes that can be written
promptly, which could even be zero. To wait for the stream to be ready to
accept data, the subscribe function to obtain a pollable which can be
polled for using wasi:io/poll.
Dropping an output-stream while there's still an active write in
progress may result in the data being lost. Before dropping the stream,
be sure to fully flush your writes.
check-write: func() -> result<u64, stream-error>since 0.2.0Check readiness for writing. This function never blocks.
Returns the number of bytes permitted for the next call to
write, or an error. Callingwritewith more bytes than this function has permitted will trap.When this function returns 0 bytes, the
subscribepollable will become ready when this function will report at least 1 byte, or an error.write: func(contents: list<u8>) -> result<_, stream-error>since 0.2.0Perform a write. This function never blocks.
When the destination of a
writeis binary data, the bytes fromcontentsare written verbatim. When the destination of awriteis known to the implementation to be text, the bytes ofcontentsare transcoded from UTF-8 into the encoding of the destination and then written.Precondition: check-write gave permit of Ok(n) and contents has a length of less than or equal to n. Otherwise, this function will trap.
returns Err(closed) without writing if the stream has closed since the last call to check-write provided a permit.
blocking-write-and-flush: func( contents: list<u8>, ) -> result<_, stream-error>since 0.2.0Perform a write of up to 4096 bytes, and then flush the stream. Block until all of these operations are complete, or an error occurs.
This is a convenience wrapper around the use of
check-write,subscribe,write, andflush, and is implemented with the following pseudo-code:let pollable = this.subscribe(); while !contents.is_empty() { // Wait for the stream to become writable pollable.block(); let Ok(n) = this.check-write(); // eliding error handling let len = min(n, contents.len()); let (chunk, rest) = contents.split_at(len); this.write(chunk ); // eliding error handling contents = rest; } this.flush(); // Wait for completion of `flush` pollable.block(); // Check for any errors that arose during `flush` let _ = this.check-write(); // eliding error handlingflush: func() -> result<_, stream-error>since 0.2.0Request to flush buffered output. This function never blocks.
This tells the output-stream that the caller intends any buffered output to be flushed. the output which is expected to be flushed is all that has been passed to
writeprior to this call.Upon calling this function, the
output-streamwill not accept any writes (check-writewill returnok(0)) until the flush has completed. Thesubscribepollable will become ready when the flush has completed and the stream can accept more writes.blocking-flush: func() -> result<_, stream-error>since 0.2.0Request to flush buffered output, and block until flush completes and stream is ready for writing again.
subscribe: func() -> pollablesince 0.2.0Create a
pollablewhich will resolve once the output-stream is ready for more writing, or an error has occurred. When this pollable is ready,check-writewill returnok(n)with n>0, or an error.If the stream is closed, this pollable is always ready immediately.
The created
pollableis a child resource of theoutput-stream. Implementations may trap if theoutput-streamis dropped before all derivedpollables created with this function are dropped.write-zeroes: func(len: u64) -> result<_, stream-error>since 0.2.0Write zeroes to a stream.
This should be used precisely like
writewith the exact same preconditions (must use check-write first), but instead of passing a list of bytes, you simply pass the number of zero-bytes that should be written.blocking-write-zeroes-and-flush: func( len: u64, ) -> result<_, stream-error>since 0.2.0Perform a write of up to 4096 zeroes, and then flush the stream. Block until all of these operations are complete, or an error occurs.
This is a convenience wrapper around the use of
check-write,subscribe,write-zeroes, andflush, and is implemented with the following pseudo-code:let pollable = this.subscribe(); while num_zeroes != 0 { // Wait for the stream to become writable pollable.block(); let Ok(n) = this.check-write(); // eliding error handling let len = min(n, num_zeroes); this.write-zeroes(len); // eliding error handling num_zeroes -= len; } this.flush(); // Wait for completion of `flush` pollable.block(); // Check for any errors that arose during `flush` let _ = this.check-write(); // eliding error handlingsplice: func( src: borrow<input-stream>, len: u64, ) -> result<u64, stream-error>since 0.2.0Read from one stream and write to another.
The behavior of splice is equivalent to:
- calling
check-writeon theoutput-stream - calling
readon theinput-streamwith the smaller of thecheck-writepermitted length and thelenprovided tosplice - calling
writeon theoutput-streamwith that read data.
Any error reported by the call to
check-write,read, orwriteends the splice and reports that error.This function returns the number of bytes transferred; it may be less than
len.- calling
blocking-splice: func( src: borrow<input-stream>, len: u64, ) -> result<u64, stream-error>since 0.2.0Read from one stream and write to another, with blocking.
This is similar to
splice, except that it blocks until theoutput-streamis ready for writing, and theinput-streamis ready for reading, before performing thesplice.
WIT
/// WASI I/O is an I/O abstraction API which is currently focused on providing
/// stream types.
///
/// In the future, the component model is expected to add built-in stream types;
/// when it does, they are expected to subsume this API.
@since(version = 0.2.0)
interface streams {
@since(version = 0.2.0)
use error.{error};
@since(version = 0.2.0)
use poll.{pollable};
/// An error for input-stream and output-stream operations.
@since(version = 0.2.0)
variant stream-error {
/// The last operation (a write or flush) failed before completion.
///
/// More information is available in the `error` payload.
///
/// After this, the stream will be closed. All future operations return
/// `stream-error::closed`.
last-operation-failed(error),
/// The stream is closed: no more input will be accepted by the
/// stream. A closed output-stream will return this error on all
/// future operations.
closed,
}
/// An input bytestream.
///
/// `input-stream`s are *non-blocking* to the extent practical on underlying
/// platforms. I/O operations always return promptly; if fewer bytes are
/// promptly available than requested, they return the number of bytes promptly
/// available, which could even be zero. To wait for data to be available,
/// use the `subscribe` function to obtain a `pollable` which can be polled
/// for using `wasi:io/poll`.
@since(version = 0.2.0)
resource input-stream {
/// Perform a non-blocking read from the stream.
///
/// When the source of a `read` is binary data, the bytes from the source
/// are returned verbatim. When the source of a `read` is known to the
/// implementation to be text, bytes containing the UTF-8 encoding of the
/// text are returned.
///
/// This function returns a list of bytes containing the read data,
/// when successful. The returned list will contain up to `len` bytes;
/// it may return fewer than requested, but not more. The list is
/// empty when no bytes are available for reading at this time. The
/// pollable given by `subscribe` will be ready when more bytes are
/// available.
///
/// This function fails with a `stream-error` when the operation
/// encounters an error, giving `last-operation-failed`, or when the
/// stream is closed, giving `closed`.
///
/// When the caller gives a `len` of 0, it represents a request to
/// read 0 bytes. If the stream is still open, this call should
/// succeed and return an empty list, or otherwise fail with `closed`.
///
/// The `len` parameter is a `u64`, which could represent a list of u8 which
/// is not possible to allocate in wasm32, or not desirable to allocate as
/// as a return value by the callee. The callee may return a list of bytes
/// less than `len` in size while more bytes are available for reading.
@since(version = 0.2.0)
read: func(len: u64) -> result<list<u8>, stream-error>;
/// Read bytes from a stream, after blocking until at least one byte can
/// be read. Except for blocking, behavior is identical to `read`.
@since(version = 0.2.0)
blocking-read: func(len: u64) -> result<list<u8>, stream-error>;
/// Skip bytes from a stream. Returns number of bytes skipped.
///
/// Behaves identical to `read`, except instead of returning a list
/// of bytes, returns the number of bytes consumed from the stream.
@since(version = 0.2.0)
skip: func(len: u64) -> result<u64, stream-error>;
/// Skip bytes from a stream, after blocking until at least one byte
/// can be skipped. Except for blocking behavior, identical to `skip`.
@since(version = 0.2.0)
blocking-skip: func(len: u64) -> result<u64, stream-error>;
/// Create a `pollable` which will resolve once either the specified stream
/// has bytes available to read or the other end of the stream has been
/// closed.
/// The created `pollable` is a child resource of the `input-stream`.
/// Implementations may trap if the `input-stream` is dropped before
/// all derived `pollable`s created with this function are dropped.
@since(version = 0.2.0)
subscribe: func() -> pollable;
}
/// An output bytestream.
///
/// `output-stream`s are *non-blocking* to the extent practical on
/// underlying platforms. Except where specified otherwise, I/O operations also
/// always return promptly, after the number of bytes that can be written
/// promptly, which could even be zero. To wait for the stream to be ready to
/// accept data, the `subscribe` function to obtain a `pollable` which can be
/// polled for using `wasi:io/poll`.
///
/// Dropping an `output-stream` while there's still an active write in
/// progress may result in the data being lost. Before dropping the stream,
/// be sure to fully flush your writes.
@since(version = 0.2.0)
resource output-stream {
/// Check readiness for writing. This function never blocks.
///
/// Returns the number of bytes permitted for the next call to `write`,
/// or an error. Calling `write` with more bytes than this function has
/// permitted will trap.
///
/// When this function returns 0 bytes, the `subscribe` pollable will
/// become ready when this function will report at least 1 byte, or an
/// error.
@since(version = 0.2.0)
check-write: func() -> result<u64, stream-error>;
/// Perform a write. This function never blocks.
///
/// When the destination of a `write` is binary data, the bytes from
/// `contents` are written verbatim. When the destination of a `write` is
/// known to the implementation to be text, the bytes of `contents` are
/// transcoded from UTF-8 into the encoding of the destination and then
/// written.
///
/// Precondition: check-write gave permit of Ok(n) and contents has a
/// length of less than or equal to n. Otherwise, this function will trap.
///
/// returns Err(closed) without writing if the stream has closed since
/// the last call to check-write provided a permit.
@since(version = 0.2.0)
write: func(contents: list<u8>) -> result<_, stream-error>;
/// Perform a write of up to 4096 bytes, and then flush the stream. Block
/// until all of these operations are complete, or an error occurs.
///
/// This is a convenience wrapper around the use of `check-write`,
/// `subscribe`, `write`, and `flush`, and is implemented with the
/// following pseudo-code:
///
/// ```text
/// let pollable = this.subscribe();
/// while !contents.is_empty() {
/// // Wait for the stream to become writable
/// pollable.block();
/// let Ok(n) = this.check-write(); // eliding error handling
/// let len = min(n, contents.len());
/// let (chunk, rest) = contents.split_at(len);
/// this.write(chunk ); // eliding error handling
/// contents = rest;
/// }
/// this.flush();
/// // Wait for completion of `flush`
/// pollable.block();
/// // Check for any errors that arose during `flush`
/// let _ = this.check-write(); // eliding error handling
/// ```
@since(version = 0.2.0)
blocking-write-and-flush: func(contents: list<u8>) -> result<_, stream-error>;
/// Request to flush buffered output. This function never blocks.
///
/// This tells the output-stream that the caller intends any buffered
/// output to be flushed. the output which is expected to be flushed
/// is all that has been passed to `write` prior to this call.
///
/// Upon calling this function, the `output-stream` will not accept any
/// writes (`check-write` will return `ok(0)`) until the flush has
/// completed. The `subscribe` pollable will become ready when the
/// flush has completed and the stream can accept more writes.
@since(version = 0.2.0)
flush: func() -> result<_, stream-error>;
/// Request to flush buffered output, and block until flush completes
/// and stream is ready for writing again.
@since(version = 0.2.0)
blocking-flush: func() -> result<_, stream-error>;
/// Create a `pollable` which will resolve once the output-stream
/// is ready for more writing, or an error has occurred. When this
/// pollable is ready, `check-write` will return `ok(n)` with n>0, or an
/// error.
///
/// If the stream is closed, this pollable is always ready immediately.
///
/// The created `pollable` is a child resource of the `output-stream`.
/// Implementations may trap if the `output-stream` is dropped before
/// all derived `pollable`s created with this function are dropped.
@since(version = 0.2.0)
subscribe: func() -> pollable;
/// Write zeroes to a stream.
///
/// This should be used precisely like `write` with the exact same
/// preconditions (must use check-write first), but instead of
/// passing a list of bytes, you simply pass the number of zero-bytes
/// that should be written.
@since(version = 0.2.0)
write-zeroes: func(len: u64) -> result<_, stream-error>;
/// Perform a write of up to 4096 zeroes, and then flush the stream.
/// Block until all of these operations are complete, or an error
/// occurs.
///
/// This is a convenience wrapper around the use of `check-write`,
/// `subscribe`, `write-zeroes`, and `flush`, and is implemented with
/// the following pseudo-code:
///
/// ```text
/// let pollable = this.subscribe();
/// while num_zeroes != 0 {
/// // Wait for the stream to become writable
/// pollable.block();
/// let Ok(n) = this.check-write(); // eliding error handling
/// let len = min(n, num_zeroes);
/// this.write-zeroes(len); // eliding error handling
/// num_zeroes -= len;
/// }
/// this.flush();
/// // Wait for completion of `flush`
/// pollable.block();
/// // Check for any errors that arose during `flush`
/// let _ = this.check-write(); // eliding error handling
/// ```
@since(version = 0.2.0)
blocking-write-zeroes-and-flush: func(len: u64) -> result<_, stream-error>;
/// Read from one stream and write to another.
///
/// The behavior of splice is equivalent to:
/// 1. calling `check-write` on the `output-stream`
/// 2. calling `read` on the `input-stream` with the smaller of the
/// `check-write` permitted length and the `len` provided to `splice`
/// 3. calling `write` on the `output-stream` with that read data.
///
/// Any error reported by the call to `check-write`, `read`, or
/// `write` ends the splice and reports that error.
///
/// This function returns the number of bytes transferred; it may be less
/// than `len`.
@since(version = 0.2.0)
splice: func(src: borrow<input-stream>, len: u64) -> result<u64, stream-error>;
/// Read from one stream and write to another, with blocking.
///
/// This is similar to `splice`, except that it blocks until the
/// `output-stream` is ready for writing, and the `input-stream`
/// is ready for reading, before performing the `splice`.
@since(version = 0.2.0)
blocking-splice: func(src: borrow<input-stream>, len: u64) -> result<u64, stream-error>;
}
}