io #
Description
io provides common interfaces for buffered reading/writing of data.
Copying bytes and streams
copy_bytes(mut dst, src) copies min(dst.len, src.len) bytes and returns that count. It leaves the destination length unchanged, ignores unused capacity, and supports overlapping slices. It can also be stored in a function value.
import io
mut bytes := 'abcdef'.bytes()
assert io.copy_bytes(mut bytes[2..], bytes[..4]) == 4
assert bytes.bytestr() == 'ababcd'
copy(mut dst, mut src)! copies from a Reader into a Writer until the reader reports io.Eof. It returns the total byte count on success and uses a 32 KiB buffer. copy_buffer(mut dst, mut src, mut buffer)! uses the supplied buffer instead. An empty buffer is replaced with a 32 KiB buffer; a nonempty buffer keeps its length.
copy_n(mut dst, mut src, n)! copies exactly n bytes without reading beyond that limit. If n <= 0, it returns zero without reading or writing. Early io.Eof returns an unexpected-EOF error.
All three stream helpers handle short reads and retry short writes. Negative or oversized counts, zero-byte reads, and zero-byte writes return errors. A zero-byte read is a no-progress error, not a completed stream. Other reader and writer errors propagate unchanged, including a writer's io.Eof. Errors may leave partial output in the writer; the result carries a byte count only on success.
Synchronous pipes
pipe() returns a connected PipeReader and PipeWriter. A nonempty read waits for data or a close. A nonempty write waits until one or more reads consume all its bytes; parallel writes are serialized. Run the producer and consumer in separate workers. Reads, writes, and closes may run concurrently. Do not modify a write's input while it runs.
Closing the writer makes reads return Eof; close_with_error(err) supplies another error. Closing the reader makes writes fail, forwarding its custom error while the writer is open. An operation on its own closed end returns a pipe-closed error. Each end retains its first close error, and closing either end wakes blocked operations. An interrupted write may have already delivered some bytes; !int carries a count only on success. Empty reads and writes are no-ops that return zero, even after a close.
Discarding output
Discard implements Writer by accepting every byte without storing data. Writes never fail and return the input length, including zero for an empty input. Copy io.discard to a mutable local, or construct io.Discard{}, when a writer is required.
import io
mut sink := io.discard
assert sink.write('ignored'.bytes())! == 7
ByteBuffer
new_byte_buffer(capacity) creates a growable cursor-based buffer. put_u8, put_u16_be/put_u16_le, put_u32_be/put_u32_le, put_u64_be/put_u64_le, and put_bytes append values. Corresponding get_* methods consume bytes and return errors on underflow, including negative or oversized byte counts, without moving the cursor.
flip() and rewind() reset the read cursor to the start. compact() discards consumed bytes, and clear() discards all bytes. remaining() counts unread bytes; len() counts all buffered bytes. There is no separate limit or write mode: puts always append and reads consume the region from the cursor through the current length.
limited_reader and section_reader cap the remaining bytes without changing the caller's buffer length. tee_reader mirrors read bytes and reports write errors or short writes. multi_reader advances only at Eof; it preserves other errors and zero-byte reads from the current reader. Reader combinators reject negative counts or counts larger than the buffer. read_full fills the buffer, propagates reader errors, and errors on a zero-byte read. read_byte reads one byte. BufferedReader.unread_byte permits one undo of a successful buffered read_byte; another read operation, peek, or failed read clears that permission.
read_exact(mut reader, mut buffer)! fills the supplied buffer across short reads. An empty buffer succeeds without reading. Early Eof or a zero-byte read returns Eof; other reader errors propagate. Negative counts or counts larger than the remaining buffer return errors. A failure may leave the bytes read so far in the buffer.
BufferedReader.lines() produces a LineIterator; next() returns a line without its newline delimiter and handles CRLF and a final line without a newline. Iteration ends with none at EOF or any read error. The iterator uses the original reader, which must remain valid until iteration finishes. Seeker describes seek(position, os.SeekMode)!; os.File implements it.
Writing strings
write_string(mut writer, text)! writes a string through a Writer. It calls write once and returns the writer's byte count or error, including a short count without retrying. Counts refer to UTF-8 bytes, not Unicode characters. Empty strings are passed to the writer too.
import io
import strings
mut buffer := strings.new_builder(16)
assert io.write_string(mut buffer, 'hello')! == 5
assert buffer.str() == 'hello'
Constants #
const discard = Discard{}
discard is a reusable Discard value. Copy it to a mutable local before writing: mut sink := io.discard.
const read_all_len = 10 * 1024
const read_all_grow_len = 1024
fn copy #
fn copy(mut dst Writer, mut src Reader) !i64
copy copies from src to dst until src reports Eof, returning the bytes copied. It uses a 32 KiB buffer and retries short writes. Other errors propagate. Invalid counts and zero-byte reads or writes return errors, possibly after partial output.
fn copy_buffer #
fn copy_buffer(mut dst Writer, mut src Reader, mut buf []u8) !i64
copy_buffer copies from src to dst using the provided buffer. An empty buffer is replaced with a 32 KiB buffer. Otherwise its length is unchanged. Reader Eof completes the copy; other errors propagate. Short writes are retried. Invalid counts and zero-byte reads or writes return errors, possibly after partial output.
fn copy_bytes #
fn copy_bytes(mut dst []u8, src []u8) int
copy_bytes copies bytes from src into dst and returns the number copied. It copies min(dst.len, src.len) bytes without growing dst. The slices may overlap.
fn copy_n #
fn copy_n(mut dst Writer, mut src Reader, n i64) !i64
copy_n copies n bytes from src to dst. A nonpositive n succeeds without reading or writing. Early Eof is an error. Short writes are retried; invalid counts and zero-byte reads or writes are errors. Errors may leave partial output in dst.
fn cp #
fn cp(mut src Reader, mut dst Writer, params CopySettings) !
cp copies from src to dst by allocating a maximum of 1024 bytes buffer for reading until either EOF is reached on src or an error occurs. An error is returned if an error is encountered during write.
fn limited_reader #
fn limited_reader(r Reader, n i64) LimitedReader
limited_reader wraps r so that at most n further bytes can be read.
fn make_readerwriter #
fn make_readerwriter(r Reader, w Writer) ReaderWriterImpl
make_readerwriter takes a rstream and a wstream and makes an rwstream with them.
fn multi_reader #
fn multi_reader(readers []Reader) MultiReader
multi_reader returns a reader over the concatenation of readers.
fn new_buffered_reader #
fn new_buffered_reader(o BufferedReaderConfig) &BufferedReader
new_buffered_reader creates a new BufferedReader.
fn new_buffered_writer #
fn new_buffered_writer(o BufferedWriterConfig) !&BufferedWriter
new_buffered_writer creates a new BufferedWriter with the specified BufferedWriterConfig. Returns an error when cap is 0 or negative.
fn new_byte_buffer #
fn new_byte_buffer(cap int) &ByteBuffer
new_byte_buffer creates an empty ByteBuffer with the given initial capacity.
fn new_multi_writer #
fn new_multi_writer(writers ...Writer) Writer
new_multi_writer returns a Writer that writes to all writers. The write function of the returned Writer writes to all writers of the MultiWriter, returns the length of bytes written, and if any writer fails to write the full length an error is returned and writing to other writers stops, and if any writer returns an error the error is returned immediately and writing to other writers stops.
fn pipe #
fn pipe() (&PipeReader, &PipeWriter)
pipe creates a synchronous in-memory pipe. Each write blocks until one or more reads consume all of its bytes or either end closes. Concurrent reads, writes, and closes are safe. Separate writes never interleave.
fn read_all #
fn read_all(config ReadAllConfig) ![]u8
read_all reads all bytes from a reader until either a 0 length read or if read_to_end_of_stream is true then the end of the stream (none).
fn read_any #
fn read_any(mut r Reader) ![]u8
read_any reads any available bytes from a reader (until the reader returns a read of 0 length).
fn read_byte #
fn read_byte(mut r Reader) !u8
read_byte returns the next byte from a reader, or Eof at the end of the stream. It is the V-idiomatic equivalent of Go's ReadByte default.
fn read_exact #
fn read_exact(mut r Reader, mut buf []u8) !
read_exact reads exactly buf.len bytes into buf, looping over short reads. It returns an Eof error if the stream ends before buf is full. Invalid read counts are rejected; other reader errors propagate unchanged.
fn read_full #
fn read_full(mut r Reader, mut buf []u8) !
read_full fills buf completely from r, returning Eof when the stream ends before buf is full. A successful zero-byte read returns a no-progress error.
fn section_reader #
fn section_reader(r Reader, count i64) SectionReader
section_reader reads exactly count bytes from r, then reports Eof.
fn tee_reader #
fn tee_reader(r Reader, w Writer) TeeReader
tee_reader wraps r so that everything read from it is also written to w.
fn write_string #
fn write_string(mut w Writer, s string) !int
write_string writes the contents of s to w, which accepts a slice of bytes. It calls w.write once and returns its byte count or error without retrying a short write. The count is in bytes, not Unicode characters.
interface RandomReader #
interface RandomReader {
read_from(pos u64, mut buf []u8) !int
}
RandomReader represents a stream of readable data from at a random location.
interface RandomWriter #
interface RandomWriter {
write_to(pos u64, buf []u8) !int
}
RandomWriter is the interface that wraps the write_to method, which writes buf.len bytes to the underlying data stream at a random pos.
interface Reader #
interface Reader {
// read reads up to buf.len bytes and places
// them into buf.
// A type that implements this should return
// `io.Eof` on end of stream (EOF) instead of just returning 0
mut:
read(mut buf []u8) !int
}
Reader represents a stream of data that can be read.
interface ReaderWriter #
interface ReaderWriter {
Reader
Writer
}
ReaderWriter represents a stream that can be read and written.
interface Seeker #
interface Seeker {
mut:
seek(pos i64, mode os.SeekMode) !
}
Seeker is implemented by streams that support random-access repositioning, e.g. os.File (see os.SeekMode for the origin values).
interface Writer #
interface Writer {
mut:
write(buf []u8) !int
}
Writer is the interface that wraps the write method, which writes buf.len bytes to the underlying data stream.
fn (ReaderWriterImpl) read #
fn (mut r ReaderWriterImpl) read(mut buf []u8) !int
read reads up to buf.len bytes into buf. It returns the number of bytes read or any error encountered.
fn (ReaderWriterImpl) write #
fn (mut r ReaderWriterImpl) write(buf []u8) !int
write writes buf.len bytes from buf to the underlying data stream. It returns the number of bytes written or any error encountered.
struct BufferedReadLineConfig #
struct BufferedReadLineConfig {
pub:
delim u8 = `\n` // line delimiter; default `\n` mode strips both `\n` and `\r\n`
}
BufferedReadLineConfig are options that can be given to the read_line() function.
struct BufferedReader #
struct BufferedReader {
mut:
reader Reader
buf []u8
offset int // current offset in the buffer
len int
can_unread_byte bool
fails int // how many times fill_buffer has read 0 bytes in a row
mfails int // maximum fails, after which we can assume that the stream has ended
pub mut:
end_of_stream bool // whether we reached the end of the upstream reader
total_read int // total number of bytes read
}
BufferedReader provides a buffered interface for a reader.
fn (BufferedReader) read #
fn (mut r BufferedReader) read(mut buf []u8) !int
read fufills the Reader interface.
fn (BufferedReader) peek #
fn (mut r BufferedReader) peek(n int) ![]u8
peek reads n bytes without advancing the cursor. returns Eof if reached the end of the stream. returns an error if n < 0. returns an array with less than n bytes if n > capacity.
fn (BufferedReader) free #
fn (mut r BufferedReader) free()
free deallocates the memory for a buffered reader's internal buffer.
fn (BufferedReader) end_of_stream #
fn (r BufferedReader) end_of_stream() bool
end_of_stream returns whether the end of the stream was reached.
fn (BufferedReader) read_line #
fn (mut r BufferedReader) read_line(config BufferedReadLineConfig) !string
read_line attempts to read a line from the buffered reader. It reads until it finds the specified delimiter or the end of stream. The returned string does not include the delimiter. With the default delimiter \n, both \n and \r\n line endings are accepted, and neither terminator byte is included in the returned string.
fn (BufferedReader) lines #
fn (mut r BufferedReader) lines() LineIterator
lines returns an iterator over the lines of the buffered reader.
fn (BufferedReader) read_byte #
fn (mut b BufferedReader) read_byte() !u8
read_byte returns the next byte from a buffered reader, or Eof at the end of the stream (like bufio.Reader.ReadByte).
fn (BufferedReader) unread_byte #
fn (mut b BufferedReader) unread_byte() !
unread_byte returns the last-read byte to the buffer, so the next read returns it again. Only one unread after a successful read_byte is allowed; other read operations, peeking, and failed reads invalidate that permission.
struct BufferedReaderConfig #
struct BufferedReaderConfig {
pub:
reader Reader
cap int = 128 * 1024 // large for fast reading of big(ish) files
retries int = 2 // how many times to retry before assuming the stream ended
}
BufferedReaderConfig are options that can be given to a buffered reader.
struct BufferedWriter #
struct BufferedWriter {
mut:
n int
wr Writer
pub mut:
buf []u8
}
fn (BufferedWriter) reset #
fn (mut b BufferedWriter) reset()
reset resets the buffer to its initial state.
fn (BufferedWriter) buffered #
fn (b BufferedWriter) buffered() int
buffered returns the number of bytes currently stored in the buffer.
fn (BufferedWriter) flush #
fn (mut b BufferedWriter) flush() !
flush writes the buffered data to the underlying writer and clears the buffer, ensures all data is written. Returns an error if the writer fails to write all buffered data.
fn (BufferedWriter) available #
fn (b BufferedWriter) available() int
available returns the amount of available space left in the buffer.
fn (BufferedWriter) write #
fn (mut b BufferedWriter) write(src []u8) !int
write writes src in the buffer, flushing it to the underlying writer as needed, and returns the number of bytes written.
struct BufferedWriterConfig #
struct BufferedWriterConfig {
pub:
writer Writer
cap int = 128 * 1024
}
struct ByteBuffer #
struct ByteBuffer {
mut:
buf []u8
pos int // read cursor; the write end is buf.len
}
ByteBuffer is a cursor-based byte buffer for protocol parsing: producers append with put_, flip switches to read mode, consumers take with get_, and compact discards consumed bytes. It is the V-idiomatic equivalent of Java's java.nio.ByteBuffer, minus the limit register: the readable region is always pos..len, and puts always append at the end. Integer codecs are hand-rolled shifts (rather than encoding.binary) to keep io dependency-free.
fn (ByteBuffer) remaining #
fn (b &ByteBuffer) remaining() int
remaining returns the number of unread bytes.
fn (ByteBuffer) len #
fn (b &ByteBuffer) len() int
len returns the total buffered bytes (read plus unread).
fn (ByteBuffer) flip #
fn (mut b ByteBuffer) flip()
flip switches from write mode to read mode: the cursor returns to the start so the appended bytes can be consumed with get_*.
fn (ByteBuffer) rewind #
fn (mut b ByteBuffer) rewind()
rewind returns the cursor to the start without discarding anything.
fn (ByteBuffer) compact #
fn (mut b ByteBuffer) compact()
compact discards consumed bytes, keeping the unread remainder.
fn (ByteBuffer) clear #
fn (mut b ByteBuffer) clear()
clear discards everything, resetting cursor and length.
fn (ByteBuffer) put_u8 #
fn (mut b ByteBuffer) put_u8(v u8)
put_u8 appends one byte.
fn (ByteBuffer) put_u16_be #
fn (mut b ByteBuffer) put_u16_be(v u16)
put_u16_be appends v big-endian.
fn (ByteBuffer) put_u16_le #
fn (mut b ByteBuffer) put_u16_le(v u16)
put_u16_le appends v little-endian.
fn (ByteBuffer) put_u32_be #
fn (mut b ByteBuffer) put_u32_be(v u32)
put_u32_be appends v big-endian.
fn (ByteBuffer) put_u32_le #
fn (mut b ByteBuffer) put_u32_le(v u32)
put_u32_le appends v little-endian.
fn (ByteBuffer) put_u64_be #
fn (mut b ByteBuffer) put_u64_be(v u64)
put_u64_be appends v big-endian.
fn (ByteBuffer) put_u64_le #
fn (mut b ByteBuffer) put_u64_le(v u64)
put_u64_le appends v little-endian.
fn (ByteBuffer) put_bytes #
fn (mut b ByteBuffer) put_bytes(data []u8)
put_bytes appends raw bytes.
fn (ByteBuffer) get_u8 #
fn (mut b ByteBuffer) get_u8() !u8
get_u8 consumes one byte.
fn (ByteBuffer) get_u16_be #
fn (mut b ByteBuffer) get_u16_be() !u16
get_u16_be consumes a big-endian u16.
fn (ByteBuffer) get_u16_le #
fn (mut b ByteBuffer) get_u16_le() !u16
get_u16_le consumes a little-endian u16.
fn (ByteBuffer) get_u32_be #
fn (mut b ByteBuffer) get_u32_be() !u32
get_u32_be consumes a big-endian u32.
fn (ByteBuffer) get_u32_le #
fn (mut b ByteBuffer) get_u32_le() !u32
get_u32_le consumes a little-endian u32.
fn (ByteBuffer) get_u64_be #
fn (mut b ByteBuffer) get_u64_be() !u64
get_u64_be consumes a big-endian u64.
fn (ByteBuffer) get_u64_le #
fn (mut b ByteBuffer) get_u64_le() !u64
get_u64_le consumes a little-endian u64.
fn (ByteBuffer) get_bytes #
fn (mut b ByteBuffer) get_bytes(n int) ![]u8
get_bytes consumes n raw bytes.
struct CopySettings #
struct CopySettings {
pub mut:
buffer_size int = 64 * 1024 // The buffer size used during the copying. A larger buffer is more performant, but uses more RAM.
}
CopySettings provides additional options to io.cp
struct Discard #
struct Discard {}
Discard is a stateless Writer that discards all data written to it.
fn (Discard) write #
fn (mut d Discard) write(buf []u8) !int
write accepts all bytes in buf without storing or modifying them. It returns buf.len, including zero for an empty buffer, and never errors.
struct Eof #
struct Eof {
Error
}
Eof error means that we reach the end of the stream.
struct LimitedReader #
struct LimitedReader {
mut:
r Reader
n i64
}
LimitedReader reads at most n further bytes from an underlying reader; after that it reports Eof. It is the V-idiomatic equivalent of Go's io.LimitedReader.
fn (LimitedReader) read #
fn (mut l LimitedReader) read(mut buf []u8) !int
read implements Reader for LimitedReader.
struct LineIterator #
struct LineIterator {
mut:
r &BufferedReader
}
LineIterator iterates over the lines of a BufferedReader, one next call per line (without the delimiter). Iteration ends with none at EOF; any read error likewise ends iteration.
fn (LineIterator) next #
fn (mut it LineIterator) next() ?string
next returns the next line, or none when the stream is exhausted.
struct MultiReader #
struct MultiReader {
mut:
readers []Reader
}
MultiReader concatenates several readers in order; when one is exhausted, the next takes over (Go's io.MultiReader).
fn (MultiReader) read #
fn (mut m MultiReader) read(mut buf []u8) !int
read implements Reader for MultiReader. Only Eof advances to the next reader; other errors and zero-byte reads leave the current reader in place.
struct MultiWriter #
struct MultiWriter {
pub mut:
writers []Writer
}
MultiWriter writes to all its writers.
fn (MultiWriter) write #
fn (mut m MultiWriter) write(buf []u8) !int
write writes to all writers of the MultiWriter. Returns the length of bytes written. If any writer fails to write the full length an error is returned and writing to other writers stops. If any writer returns an error the error is returned immediately and writing to other writers stops.
struct NotExpected #
struct NotExpected {
cause string
code int
}
NotExpected is a generic error that means that we receave a not expected error.
struct PipeReader #
struct PipeReader {
mut:
state &PipeState
}
PipeReader is the read half of an in-memory pipe.
fn (PipeReader) read #
fn (mut r PipeReader) read(mut buf []u8) !int
read waits for data or a close. A closed writer yields its first error, or Eof. A closed reader yields a pipe-closed error. Empty reads always return zero.
fn (PipeReader) close #
fn (mut r PipeReader) close()
close closes the reader; subsequent writes return an error.
fn (PipeReader) close_with_error #
fn (mut r PipeReader) close_with_error(err IError)
close_with_error closes the reader and wakes blocked operations. Writes receive err while the writer remains open. The first reader error is retained.
struct PipeWriter #
struct PipeWriter {
mut:
state &PipeState
}
PipeWriter is the write half of an in-memory pipe.
fn (PipeWriter) write #
fn (mut w PipeWriter) write(buf []u8) !int
write waits until readers consume every byte, returning the full count on success. A close may interrupt it after partial consumption; the error result has no byte count. Empty writes always return zero. The caller must not mutate buf until write returns.
fn (PipeWriter) close #
fn (mut w PipeWriter) close()
close closes the writer; subsequent reads return Eof.
fn (PipeWriter) close_with_error #
fn (mut w PipeWriter) close_with_error(err IError)
close_with_error closes the writer and wakes blocked operations. Reads receive err while the reader remains open. The first writer error is retained.
struct ReadAllConfig #
struct ReadAllConfig {
pub:
read_to_end_of_stream bool
reader Reader
}
ReadAllConfig allows options to be passed for the behaviour of read_all.
struct SectionReader #
struct SectionReader {
mut:
r Reader
count i64
off i64
}
SectionReader reads a fixed window of an underlying reader and then reports Eof, without the RandomReader tracking of Go's io.SectionReader (read-only direction).
fn (SectionReader) read #
fn (mut s SectionReader) read(mut buf []u8) !int
read implements Reader for SectionReader.
struct TeeReader #
struct TeeReader {
mut:
r Reader
w Writer
}
TeeReader mirrors every byte read into a writer as well as returning it. Write failures surface through read, like Go's io.TeeReader.
fn (TeeReader) read #
fn (mut t TeeReader) read(mut buf []u8) !int
read implements Reader for TeeReader.
- README
- Constants
- fn copy
- fn copy_buffer
- fn copy_bytes
- fn copy_n
- fn cp
- fn limited_reader
- fn make_readerwriter
- fn multi_reader
- fn new_buffered_reader
- fn new_buffered_writer
- fn new_byte_buffer
- fn new_multi_writer
- fn pipe
- fn read_all
- fn read_any
- fn read_byte
- fn read_exact
- fn read_full
- fn section_reader
- fn tee_reader
- fn write_string
- interface RandomReader
- interface RandomWriter
- interface Reader
- interface ReaderWriter
- interface Seeker
- interface Writer
- type ReaderWriterImpl
- struct BufferedReadLineConfig
- struct BufferedReader
- struct BufferedReaderConfig
- struct BufferedWriter
- struct BufferedWriterConfig
- struct ByteBuffer
- struct CopySettings
- struct Discard
- struct Eof
- struct LimitedReader
- struct LineIterator
- struct MultiReader
- struct MultiWriter
- struct NotExpected
- struct PipeReader
- struct PipeWriter
- struct ReadAllConfig
- struct SectionReader
- struct TeeReader