295 lines
7.7 KiB
Markdown
295 lines
7.7 KiB
Markdown
# std.Io - I/O API Reference (Zig 0.16.0)
|
|
|
|
Primary release-note source: https://ziglang.org/download/0.16.0/release-notes.html
|
|
|
|
Zig 0.16 makes I/O an explicit interface. All file, network, process, time, entropy, cancelable synchronization, and task APIs that can block or interact with the outside world need a `std.Io`.
|
|
|
|
## Ownership Rule
|
|
|
|
Prefer this flow:
|
|
|
|
1. Application entry receives `init: std.process.Init`.
|
|
2. It extracts `const io = init.io`.
|
|
3. It passes `io` through setup.
|
|
4. Long-lived systems that need I/O store `io`.
|
|
5. Tests use `std.testing.io`.
|
|
|
|
```zig
|
|
pub fn main(init: std.process.Init) !void {
|
|
const gpa = init.gpa;
|
|
const io = init.io;
|
|
_ = .{ gpa, io };
|
|
}
|
|
```
|
|
|
|
Temporary adapter only at a boundary:
|
|
|
|
```zig
|
|
var threaded: std.Io.Threaded = .init_single_threaded;
|
|
const io = threaded.io();
|
|
```
|
|
|
|
## Implementations
|
|
|
|
- `std.Io.Threaded`: threaded, feature-complete, closest to old blocking behavior.
|
|
- `std.Io.Evented`: experimental evented/M:N implementation.
|
|
- `std.Io.Uring`, `std.Io.Kqueue`, `std.Io.Dispatch`: platform/proof-of-concept backends.
|
|
- `std.Io.failing`: backend that supports no operations.
|
|
|
|
## Writer
|
|
|
|
`std.Io.Writer` is non-generic. It contains its buffer and vtable.
|
|
|
|
### Fixed Buffer Writer
|
|
|
|
```zig
|
|
var buffer: [256]u8 = undefined;
|
|
var writer: std.Io.Writer = .fixed(&buffer);
|
|
|
|
try writer.print("name={s} value={d}", .{ "answer", 42 });
|
|
const bytes = writer.buffered();
|
|
```
|
|
|
|
### File Writer
|
|
|
|
```zig
|
|
const file = try std.Io.Dir.cwd().createFile(io, "out.txt", .{});
|
|
defer file.close(io);
|
|
|
|
var buffer: [4096]u8 = undefined;
|
|
var file_writer = file.writer(io, &buffer);
|
|
const writer = &file_writer.interface;
|
|
|
|
try writer.writeAll("hello\n");
|
|
try writer.print("value={d}\n", .{42});
|
|
try writer.flush();
|
|
```
|
|
|
|
`flush` is still required for buffered file/socket writers.
|
|
|
|
### Standard Output
|
|
|
|
```zig
|
|
var buffer: [4096]u8 = undefined;
|
|
var stdout_writer = std.Io.File.stdout().writer(io, &buffer);
|
|
const stdout = &stdout_writer.interface;
|
|
|
|
try stdout.print("hello\n", .{});
|
|
try stdout.flush();
|
|
```
|
|
|
|
### Allocating Writer
|
|
|
|
```zig
|
|
var aw: std.Io.Writer.Allocating = .init(allocator);
|
|
defer aw.deinit();
|
|
|
|
try aw.writer.print("hello {s}", .{"world"});
|
|
const owned = try aw.toOwnedSlice();
|
|
defer allocator.free(owned);
|
|
```
|
|
|
|
`Allocating` has an `alignment` field in 0.16. Prefer `.init`, `.initAligned`, or `.initCapacity`.
|
|
|
|
### Discarding Writer
|
|
|
|
Use this when old code used counting/discarding writer patterns.
|
|
|
|
```zig
|
|
var buffer: [256]u8 = undefined;
|
|
var discarding: std.Io.Writer.Discarding = .init(&buffer);
|
|
try discarding.writer.print("ignored {d}", .{123});
|
|
const count = discarding.fullCount();
|
|
```
|
|
|
|
## Reader
|
|
|
|
`std.Io.Reader` is non-generic and buffer-aware.
|
|
|
|
### Fixed Reader
|
|
|
|
```zig
|
|
var reader: std.Io.Reader = .fixed("alpha\nbeta\n");
|
|
|
|
while (try reader.takeDelimiter('\n')) |line| {
|
|
_ = line;
|
|
}
|
|
```
|
|
|
|
`takeDelimiter` returns `!?[]u8`: `null` means EOF with no buffered bytes remaining. A final unterminated line is returned as data before a later call yields `null`.
|
|
|
|
### File Reader
|
|
|
|
```zig
|
|
const file = try std.Io.Dir.cwd().openFile(io, "data.txt", .{});
|
|
defer file.close(io);
|
|
|
|
var buffer: [4096]u8 = undefined;
|
|
var file_reader = file.reader(io, &buffer);
|
|
const reader = &file_reader.interface;
|
|
|
|
while (try reader.takeDelimiter('\n')) |line| {
|
|
_ = line;
|
|
}
|
|
```
|
|
|
|
### Read Remaining
|
|
|
|
```zig
|
|
const bytes = try reader.allocRemaining(allocator, .limited(1024 * 1024));
|
|
defer allocator.free(bytes);
|
|
```
|
|
|
|
### Binary Reads
|
|
|
|
```zig
|
|
const value = try reader.takeInt(u32, .little);
|
|
const header = try reader.takeStruct(Header, .little);
|
|
const leb = try reader.takeLeb128(u64);
|
|
```
|
|
|
|
`takeStruct` reads an extern/packed memory representation through the reader buffer; use a layout with a defined byte representation, ensure the reader can supply the full size, and do not treat a native-layout struct as a portable wire format.
|
|
|
|
## File Integration
|
|
|
|
Use `std.Io.Dir` and `std.Io.File`.
|
|
|
|
```zig
|
|
const cwd = std.Io.Dir.cwd();
|
|
|
|
const contents = try cwd.readFileAlloc(io, "data.txt", allocator, .limited(1024 * 1024));
|
|
defer allocator.free(contents);
|
|
|
|
try cwd.writeFile(io, .{
|
|
.sub_path = "out.txt",
|
|
.data = contents,
|
|
});
|
|
```
|
|
|
|
`close`, `stat`, `setTimestamps`, readers, writers, and most operations now take `io`.
|
|
|
|
## Networking Integration
|
|
|
|
Use `std.Io.net` for sockets/streams and pass `io` to high-level clients.
|
|
|
|
```zig
|
|
var client: std.http.Client = .{
|
|
.allocator = allocator,
|
|
.io = io,
|
|
};
|
|
defer client.deinit();
|
|
```
|
|
|
|
## Process Integration
|
|
|
|
Use `std.process.run` and `std.process.spawn`.
|
|
|
|
```zig
|
|
const result = try std.process.run(allocator, io, .{
|
|
.argv = &.{ "zig", "version" },
|
|
.stdout_limit = .limited(16 * 1024),
|
|
.stderr_limit = .limited(16 * 1024),
|
|
});
|
|
defer allocator.free(result.stdout);
|
|
defer allocator.free(result.stderr);
|
|
```
|
|
|
|
## Entropy
|
|
|
|
```zig
|
|
var bytes: [32]u8 = undefined;
|
|
io.random(&bytes);
|
|
|
|
const source: std.Random.IoSource = .{ .io = io };
|
|
const rng = source.interface();
|
|
```
|
|
|
|
Use `io.randomSecure` for fresh secure entropy with error reporting.
|
|
|
|
## Time
|
|
|
|
Timestamp reads now require an explicit clock choice:
|
|
|
|
```zig
|
|
const now = std.Io.Timestamp.now(io, .real);
|
|
const elapsed_mark = std.Io.Timestamp.now(io, .awake);
|
|
```
|
|
|
|
`Timestamp` is not a one-for-one replacement for every old `Instant`/`Timer` behavior; choose `.real` versus an appropriate monotonic clock such as `.awake`, and build elapsed-time helpers around that choice.
|
|
|
|
Use a shared application helper when common timestamp reads require consistent clock selection or conversion semantics.
|
|
|
|
## Tasks and Cancelation
|
|
|
|
`std.Io` provides function-level and operation-level concurrency:
|
|
|
|
- `io.async(...)`
|
|
- `std.Io.Group`
|
|
- `std.Io.Select(U)` where `U` is the tagged union of possible results
|
|
- `std.Io.Batch`
|
|
- `std.Io.Queue(Elem)`, initialized with caller-provided typed element storage
|
|
|
|
Cancelation guidance:
|
|
|
|
- Propagate `error.Canceled` by default.
|
|
- Only the code that requested cancelation should ignore it.
|
|
- If handling it locally but continuing, use `io.recancel()` when cancelation should stay active.
|
|
- Use `errdefer group.cancel(io)` after creating grouped tasks.
|
|
|
|
```zig
|
|
var group: std.Io.Group = .init;
|
|
errdefer group.cancel(io);
|
|
|
|
group.async(io, worker, .{ io, arg });
|
|
try group.await(io);
|
|
```
|
|
|
|
## Sync Primitives
|
|
|
|
Blocking sync moved to `std.Io` equivalents:
|
|
|
|
| Old | New |
|
|
|-----|-----|
|
|
| `std.Thread.Mutex` | `std.Io.Mutex` |
|
|
| `std.Thread.Condition` | `std.Io.Condition` |
|
|
| `std.Thread.Semaphore` | `std.Io.Semaphore` |
|
|
| `std.Thread.RwLock` | `std.Io.RwLock` |
|
|
| `std.Thread.ResetEvent` | `std.Io.Event` |
|
|
| `std.Thread.WaitGroup` | Conceptually `std.Io.Group`; submit tasks, then await or cancel the group |
|
|
| `std.Thread.Futex` | `io.futexWait*` / `io.futexWake`; waits have cancelable and uncancelable forms |
|
|
|
|
```zig
|
|
try mutex.lock(io);
|
|
defer mutex.unlock(io);
|
|
```
|
|
|
|
```zig
|
|
mutex.lockUncancelable(io);
|
|
defer mutex.unlock(io);
|
|
```
|
|
|
|
## Removed Reader/Writer Names
|
|
|
|
Use these replacements:
|
|
|
|
| Old | New |
|
|
|-----|-----|
|
|
| `std.io` | `std.Io` |
|
|
| `std.Io.GenericReader` | `std.Io.Reader` |
|
|
| `std.Io.AnyReader` | `std.Io.Reader` |
|
|
| `std.Io.GenericWriter` | `std.Io.Writer` |
|
|
| `std.Io.AnyWriter` | `std.Io.Writer` |
|
|
| `std.io.fixedBufferStream(data).reader()` | `var r: std.Io.Reader = .fixed(data)` |
|
|
| `std.io.fixedBufferStream(buffer).writer()` | `var w: std.Io.Writer = .fixed(buffer)` |
|
|
| `std.leb.readUleb128` / `readIleb128` | `std.Io.Reader.takeLeb128` |
|
|
|
|
## Review Checklist
|
|
|
|
- Does this API need an `io: std.Io` parameter?
|
|
- Is `io` obtained at the application/test boundary rather than constructed deep inside?
|
|
- Are all file/socket/process/time/random operations routed through `std.Io`?
|
|
- Are blocking locks using `std.Io` primitives?
|
|
- Are buffered writers flushed?
|
|
- Are cancelation errors propagated unless this code requested cancelation?
|
|
- Are temporary `Io.Threaded.init_single_threaded` adapters isolated and documented?
|