zig/lib/std/io/Writer.zig

181 lines
5.3 KiB
Zig

const std = @import("../std.zig");
const assert = std.debug.assert;
const Writer = @This();
pub const Null = @import("Writer/Null.zig");
context: ?*anyopaque,
vtable: *const VTable,
pub const VTable = struct {
/// Each slice in `data` is written in order.
///
/// `data.len` must be greater than zero, and the last element of `data` is
/// special. It is repeated as necessary so that it is written `splat`
/// number of times.
///
/// Number of bytes actually written is returned.
///
/// Number of bytes returned may be zero, which does not mean
/// end-of-stream. A subsequent call may return nonzero, or may signal end
/// of stream via `error.WriteFailed`.
writeSplat: *const fn (ctx: ?*anyopaque, data: []const []const u8, splat: usize) Error!usize,
/// Writes contents from an open file. `headers` are written first, then `len`
/// bytes of `file` starting from `offset`, then `trailers`.
///
/// Number of bytes actually written is returned, which may lie within
/// headers, the file, trailers, or anywhere in between.
///
/// Number of bytes returned may be zero, which does not mean
/// end-of-stream. A subsequent call may return nonzero, or may signal end
/// of stream via `error.WriteFailed`.
///
/// If `error.Unimplemented` is returned, the caller should do its own
/// reads from the file. The callee indicates it cannot offer a more
/// efficient implementation.
writeFile: *const fn (
ctx: ?*anyopaque,
file: std.fs.File,
/// If this is `Offset.none`, `file` will be streamed, affecting the
/// seek position. Otherwise, it will be read positionally without
/// affecting the seek position. `error.Unseekable` is only possible
/// when reading positionally.
///
/// An offset past the end of the file is treated the same as an offset
/// equal to the end of the file.
offset: Offset,
/// Maximum amount of bytes to read from the file. Implementations may
/// assume that the file size does not exceed this amount.
limit: Limit,
/// Headers and trailers must be passed together so that in case `len` is
/// zero, they can be forwarded directly to `VTable.writeVec`.
headers_and_trailers: []const []const u8,
headers_len: usize,
) FileError!usize,
};
pub const Error = error{
/// See the `Writer` implementation for detailed diagnostics.
WriteFailed,
};
pub const FileError = std.fs.File.PReadError || error{
/// See the `Writer` implementation for detailed diagnostics.
WriteFailed,
/// Indicates the caller should do its own file reading; the callee cannot
/// offer a more efficient implementation.
Unimplemented,
};
pub const Limit = std.io.Reader.Limit;
pub const Offset = enum(u64) {
zero = 0,
/// Indicates to read the file as a stream.
none = std.math.maxInt(u64),
_,
pub fn init(integer: u64) Offset {
const result: Offset = @enumFromInt(integer);
assert(result != .none);
return result;
}
pub fn toInt(o: Offset) ?u64 {
return if (o == .none) null else @intFromEnum(o);
}
pub fn advance(o: Offset, amount: u64) Offset {
return switch (o) {
.none => .none,
else => .init(@intFromEnum(o) + amount),
};
}
};
pub fn writeVec(w: Writer, data: []const []const u8) Error!usize {
assert(data.len > 0);
return w.vtable.writeSplat(w.context, data, 1);
}
pub fn writeSplat(w: Writer, data: []const []const u8, splat: usize) Error!usize {
assert(data.len > 0);
return w.vtable.writeSplat(w.context, data, splat);
}
pub fn writeFile(
w: Writer,
file: std.fs.File,
offset: Offset,
limit: Limit,
headers_and_trailers: []const []const u8,
headers_len: usize,
) FileError!usize {
return w.vtable.writeFile(w.context, file, offset, limit, headers_and_trailers, headers_len);
}
pub fn buffered(w: Writer, buffer: []u8) std.io.BufferedWriter {
return .{
.buffer = buffer,
.unbuffered_writer = w,
};
}
pub fn unbuffered(w: Writer) std.io.BufferedWriter {
return w.buffered(&.{});
}
pub fn failingWriteSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Error!usize {
_ = context;
_ = data;
_ = splat;
return error.WriteFailed;
}
pub fn failingWriteFile(
context: ?*anyopaque,
file: std.fs.File,
offset: std.io.Writer.Offset,
limit: std.io.Writer.Limit,
headers_and_trailers: []const []const u8,
headers_len: usize,
) FileError!usize {
_ = context;
_ = file;
_ = offset;
_ = limit;
_ = headers_and_trailers;
_ = headers_len;
return error.WriteFailed;
}
pub const failing: Writer = .{
.context = undefined,
.vtable = &.{
.writeSplat = failingWriteSplat,
.writeFile = failingWriteFile,
},
};
pub fn unimplementedWriteFile(
context: ?*anyopaque,
file: std.fs.File,
offset: std.io.Writer.Offset,
limit: std.io.Writer.Limit,
headers_and_trailers: []const []const u8,
headers_len: usize,
) FileError!usize {
_ = context;
_ = file;
_ = offset;
_ = limit;
_ = headers_and_trailers;
_ = headers_len;
return error.Unimplemented;
}
test {
_ = Null;
}