mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 06:15:21 +00:00
Merge pull request #17634 from ianprime0509/type-erased-writer
Add type-erased writer and GenericWriter
This commit is contained in:
commit
3122fd0ba0
@ -265,7 +265,7 @@ set(ZIG_STAGE2_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/io/limited_reader.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/io/Reader.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/io/seekable_stream.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/io/writer.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/io/Writer.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/json.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/json/stringify.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/leb128.zig"
|
||||
|
||||
@ -1986,7 +1986,10 @@ pub const BufPrintError = error{
|
||||
/// Returns a slice of the bytes printed to.
|
||||
pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![]u8 {
|
||||
var fbs = std.io.fixedBufferStream(buf);
|
||||
try format(fbs.writer(), fmt, args);
|
||||
format(fbs.writer().any(), fmt, args) catch |err| switch (err) {
|
||||
error.NoSpaceLeft => return error.NoSpaceLeft,
|
||||
else => unreachable,
|
||||
};
|
||||
return fbs.getWritten();
|
||||
}
|
||||
|
||||
@ -1998,7 +2001,7 @@ pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErr
|
||||
/// Count the characters needed for format. Useful for preallocating memory
|
||||
pub fn count(comptime fmt: []const u8, args: anytype) u64 {
|
||||
var counting_writer = std.io.countingWriter(std.io.null_writer);
|
||||
format(counting_writer.writer(), fmt, args) catch |err| switch (err) {};
|
||||
format(counting_writer.writer().any(), fmt, args) catch unreachable;
|
||||
return counting_writer.bytes_written;
|
||||
}
|
||||
|
||||
|
||||
@ -333,13 +333,73 @@ pub fn GenericReader(
|
||||
};
|
||||
}
|
||||
|
||||
pub fn GenericWriter(
|
||||
comptime Context: type,
|
||||
comptime WriteError: type,
|
||||
comptime writeFn: fn (context: Context, bytes: []const u8) WriteError!usize,
|
||||
) type {
|
||||
return struct {
|
||||
context: Context,
|
||||
|
||||
const Self = @This();
|
||||
pub const Error = WriteError;
|
||||
|
||||
pub inline fn write(self: Self, bytes: []const u8) Error!usize {
|
||||
return writeFn(self.context, bytes);
|
||||
}
|
||||
|
||||
pub inline fn writeAll(self: Self, bytes: []const u8) Error!void {
|
||||
return @errorCast(self.any().writeAll(bytes));
|
||||
}
|
||||
|
||||
pub inline fn print(self: Self, comptime format: []const u8, args: anytype) Error!void {
|
||||
return @errorCast(self.any().print(format, args));
|
||||
}
|
||||
|
||||
pub inline fn writeByte(self: Self, byte: u8) Error!void {
|
||||
return @errorCast(self.any().writeByte(byte));
|
||||
}
|
||||
|
||||
pub inline fn writeByteNTimes(self: Self, byte: u8, n: usize) Error!void {
|
||||
return @errorCast(self.any().writeByteNTimes(byte, n));
|
||||
}
|
||||
|
||||
pub inline fn writeBytesNTimes(self: Self, bytes: []const u8, n: usize) Error!void {
|
||||
return @errorCast(self.any().writeBytesNTimes(bytes, n));
|
||||
}
|
||||
|
||||
pub inline fn writeInt(self: Self, comptime T: type, value: T, endian: std.builtin.Endian) Error!void {
|
||||
return @errorCast(self.any().writeInt(T, value, endian));
|
||||
}
|
||||
|
||||
pub inline fn writeStruct(self: Self, value: anytype) Error!void {
|
||||
return @errorCast(self.any().writeStruct(value));
|
||||
}
|
||||
|
||||
pub inline fn any(self: *const Self) AnyWriter {
|
||||
return .{
|
||||
.context = @ptrCast(&self.context),
|
||||
.writeFn = typeErasedWriteFn,
|
||||
};
|
||||
}
|
||||
|
||||
fn typeErasedWriteFn(context: *const anyopaque, bytes: []const u8) anyerror!usize {
|
||||
const ptr: *const Context = @alignCast(@ptrCast(context));
|
||||
return writeFn(ptr.*, bytes);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Deprecated; consider switching to `AnyReader` or use `GenericReader`
|
||||
/// to use previous API.
|
||||
pub const Reader = GenericReader;
|
||||
/// Deprecated; consider switching to `AnyWriter` or use `GenericWriter`
|
||||
/// to use previous API.
|
||||
pub const Writer = GenericWriter;
|
||||
|
||||
pub const AnyReader = @import("io/Reader.zig");
|
||||
pub const AnyWriter = @import("io/Writer.zig");
|
||||
|
||||
pub const Writer = @import("io/writer.zig").Writer;
|
||||
pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
|
||||
|
||||
pub const BufferedWriter = @import("io/buffered_writer.zig").BufferedWriter;
|
||||
@ -652,6 +712,7 @@ pub fn PollFiles(comptime StreamEnum: type) type {
|
||||
|
||||
test {
|
||||
_ = AnyReader;
|
||||
_ = AnyWriter;
|
||||
_ = @import("io/bit_reader.zig");
|
||||
_ = @import("io/bit_writer.zig");
|
||||
_ = @import("io/buffered_atomic_file.zig");
|
||||
@ -661,7 +722,6 @@ test {
|
||||
_ = @import("io/counting_writer.zig");
|
||||
_ = @import("io/counting_reader.zig");
|
||||
_ = @import("io/fixed_buffer_stream.zig");
|
||||
_ = @import("io/writer.zig");
|
||||
_ = @import("io/peek_stream.zig");
|
||||
_ = @import("io/seekable_stream.zig");
|
||||
_ = @import("io/stream_source.zig");
|
||||
|
||||
60
lib/std/io/Writer.zig
Normal file
60
lib/std/io/Writer.zig
Normal file
@ -0,0 +1,60 @@
|
||||
const std = @import("../std.zig");
|
||||
const assert = std.debug.assert;
|
||||
const mem = std.mem;
|
||||
|
||||
context: *const anyopaque,
|
||||
writeFn: *const fn (context: *const anyopaque, bytes: []const u8) anyerror!usize,
|
||||
|
||||
const Self = @This();
|
||||
pub const Error = anyerror;
|
||||
|
||||
pub fn write(self: Self, bytes: []const u8) anyerror!usize {
|
||||
return self.writeFn(self.context, bytes);
|
||||
}
|
||||
|
||||
pub fn writeAll(self: Self, bytes: []const u8) anyerror!void {
|
||||
var index: usize = 0;
|
||||
while (index != bytes.len) {
|
||||
index += try self.write(bytes[index..]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(self: Self, comptime format: []const u8, args: anytype) anyerror!void {
|
||||
return std.fmt.format(self, format, args);
|
||||
}
|
||||
|
||||
pub fn writeByte(self: Self, byte: u8) anyerror!void {
|
||||
const array = [1]u8{byte};
|
||||
return self.writeAll(&array);
|
||||
}
|
||||
|
||||
pub fn writeByteNTimes(self: Self, byte: u8, n: usize) anyerror!void {
|
||||
var bytes: [256]u8 = undefined;
|
||||
@memset(bytes[0..], byte);
|
||||
|
||||
var remaining: usize = n;
|
||||
while (remaining > 0) {
|
||||
const to_write = @min(remaining, bytes.len);
|
||||
try self.writeAll(bytes[0..to_write]);
|
||||
remaining -= to_write;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeBytesNTimes(self: Self, bytes: []const u8, n: usize) anyerror!void {
|
||||
var i: usize = 0;
|
||||
while (i < n) : (i += 1) {
|
||||
try self.writeAll(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn writeInt(self: Self, comptime T: type, value: T, endian: std.builtin.Endian) anyerror!void {
|
||||
var bytes: [@divExact(@typeInfo(T).Int.bits, 8)]u8 = undefined;
|
||||
mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian);
|
||||
return self.writeAll(&bytes);
|
||||
}
|
||||
|
||||
pub fn writeStruct(self: Self, value: anytype) anyerror!void {
|
||||
// Only extern and packed structs have defined in-memory layout.
|
||||
comptime assert(@typeInfo(@TypeOf(value)).Struct.layout != .Auto);
|
||||
return self.writeAll(mem.asBytes(&value));
|
||||
}
|
||||
@ -1,67 +0,0 @@
|
||||
const std = @import("../std.zig");
|
||||
const assert = std.debug.assert;
|
||||
const mem = std.mem;
|
||||
|
||||
pub fn Writer(
|
||||
comptime Context: type,
|
||||
comptime WriteError: type,
|
||||
comptime writeFn: fn (context: Context, bytes: []const u8) WriteError!usize,
|
||||
) type {
|
||||
return struct {
|
||||
context: Context,
|
||||
|
||||
const Self = @This();
|
||||
pub const Error = WriteError;
|
||||
|
||||
pub fn write(self: Self, bytes: []const u8) Error!usize {
|
||||
return writeFn(self.context, bytes);
|
||||
}
|
||||
|
||||
pub fn writeAll(self: Self, bytes: []const u8) Error!void {
|
||||
var index: usize = 0;
|
||||
while (index != bytes.len) {
|
||||
index += try self.write(bytes[index..]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(self: Self, comptime format: []const u8, args: anytype) Error!void {
|
||||
return std.fmt.format(self, format, args);
|
||||
}
|
||||
|
||||
pub fn writeByte(self: Self, byte: u8) Error!void {
|
||||
const array = [1]u8{byte};
|
||||
return self.writeAll(&array);
|
||||
}
|
||||
|
||||
pub fn writeByteNTimes(self: Self, byte: u8, n: usize) Error!void {
|
||||
var bytes: [256]u8 = undefined;
|
||||
@memset(bytes[0..], byte);
|
||||
|
||||
var remaining: usize = n;
|
||||
while (remaining > 0) {
|
||||
const to_write = @min(remaining, bytes.len);
|
||||
try self.writeAll(bytes[0..to_write]);
|
||||
remaining -= to_write;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeBytesNTimes(self: Self, bytes: []const u8, n: usize) Error!void {
|
||||
var i: usize = 0;
|
||||
while (i < n) : (i += 1) {
|
||||
try self.writeAll(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn writeInt(self: Self, comptime T: type, value: T, endian: std.builtin.Endian) Error!void {
|
||||
var bytes: [@divExact(@typeInfo(T).Int.bits, 8)]u8 = undefined;
|
||||
mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian);
|
||||
return self.writeAll(&bytes);
|
||||
}
|
||||
|
||||
pub fn writeStruct(self: Self, value: anytype) Error!void {
|
||||
// Only extern and packed structs have defined in-memory layout.
|
||||
comptime assert(@typeInfo(@TypeOf(value)).Struct.layout != .Auto);
|
||||
return self.writeAll(mem.asBytes(&value));
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -306,7 +306,7 @@ pub const Function = struct {
|
||||
const ty = f.typeOf(ref);
|
||||
|
||||
const result: CValue = if (lowersToArray(ty, mod)) result: {
|
||||
const writer = f.object.code_header.writer();
|
||||
const writer = f.object.codeHeaderWriter();
|
||||
const alignment: Alignment = .none;
|
||||
const decl_c_value = try f.allocLocalValue(ty, alignment);
|
||||
const gpa = f.object.dg.gpa;
|
||||
@ -534,6 +534,10 @@ pub const Object = struct {
|
||||
fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer {
|
||||
return o.indent_writer.writer();
|
||||
}
|
||||
|
||||
fn codeHeaderWriter(o: *Object) ArrayListWriter {
|
||||
return arrayListWriter(&o.code_header);
|
||||
}
|
||||
};
|
||||
|
||||
/// This data is available both when outputting .c code and when outputting an .h file.
|
||||
@ -557,6 +561,10 @@ pub const DeclGen = struct {
|
||||
flush,
|
||||
};
|
||||
|
||||
fn fwdDeclWriter(dg: *DeclGen) ArrayListWriter {
|
||||
return arrayListWriter(&dg.fwd_decl);
|
||||
}
|
||||
|
||||
fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
|
||||
@setCold(true);
|
||||
const mod = dg.module;
|
||||
@ -1982,7 +1990,7 @@ pub const DeclGen = struct {
|
||||
fwd_kind: enum { tentative, final },
|
||||
) !void {
|
||||
const decl = dg.module.declPtr(decl_index);
|
||||
const fwd = dg.fwd_decl.writer();
|
||||
const fwd = dg.fwdDeclWriter();
|
||||
const is_global = variable.is_extern or dg.declIsGlobal(.{ .ty = decl.ty, .val = decl.val });
|
||||
try fwd.writeAll(if (is_global) "zig_extern " else "static ");
|
||||
const maybe_exports = dg.module.decl_exports.get(decl_index);
|
||||
@ -2668,7 +2676,7 @@ fn genExports(o: *Object) !void {
|
||||
};
|
||||
const decl = mod.declPtr(decl_index);
|
||||
const tv: TypedValue = .{ .ty = decl.ty, .val = Value.fromInterned((try decl.internValue(mod))) };
|
||||
const fwd = o.dg.fwd_decl.writer();
|
||||
const fwd = o.dg.fwdDeclWriter();
|
||||
|
||||
const exports = mod.decl_exports.get(decl_index) orelse return;
|
||||
if (exports.items.len < 2) return;
|
||||
@ -2782,7 +2790,7 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
|
||||
const fn_cty = try o.dg.typeToCType(fn_decl.ty, .complete);
|
||||
const fn_info = fn_cty.cast(CType.Payload.Function).?.data;
|
||||
|
||||
const fwd_decl_writer = o.dg.fwd_decl.writer();
|
||||
const fwd_decl_writer = o.dg.fwdDeclWriter();
|
||||
try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)});
|
||||
try o.dg.renderFunctionSignature(
|
||||
fwd_decl_writer,
|
||||
@ -2824,7 +2832,7 @@ pub fn genFunc(f: *Function) !void {
|
||||
defer o.code_header.deinit();
|
||||
|
||||
const is_global = o.dg.declIsGlobal(tv);
|
||||
const fwd_decl_writer = o.dg.fwd_decl.writer();
|
||||
const fwd_decl_writer = o.dg.fwdDeclWriter();
|
||||
try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");
|
||||
|
||||
if (mod.decl_exports.get(decl_index)) |exports|
|
||||
@ -2879,7 +2887,7 @@ pub fn genFunc(f: *Function) !void {
|
||||
};
|
||||
free_locals.sort(SortContext{ .keys = free_locals.keys() });
|
||||
|
||||
const w = o.code_header.writer();
|
||||
const w = o.codeHeaderWriter();
|
||||
for (free_locals.values()) |list| {
|
||||
for (list.keys()) |local_index| {
|
||||
const local = f.locals.items[local_index];
|
||||
@ -2907,7 +2915,7 @@ pub fn genDecl(o: *Object) !void {
|
||||
|
||||
if (!tv.ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) return;
|
||||
if (tv.val.getExternFunc(mod)) |_| {
|
||||
const fwd_decl_writer = o.dg.fwd_decl.writer();
|
||||
const fwd_decl_writer = o.dg.fwdDeclWriter();
|
||||
try fwd_decl_writer.writeAll("zig_extern ");
|
||||
try o.dg.renderFunctionSignature(fwd_decl_writer, decl_index, .forward, .{ .export_index = 0 });
|
||||
try fwd_decl_writer.writeAll(";\n");
|
||||
@ -2948,7 +2956,7 @@ pub fn genDeclValue(
|
||||
link_section: InternPool.OptionalNullTerminatedString,
|
||||
) !void {
|
||||
const mod = o.dg.module;
|
||||
const fwd_decl_writer = o.dg.fwd_decl.writer();
|
||||
const fwd_decl_writer = o.dg.fwdDeclWriter();
|
||||
|
||||
try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");
|
||||
try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, Const, alignment, .complete);
|
||||
@ -2992,7 +3000,7 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
|
||||
.ty = decl.ty,
|
||||
.val = decl.val,
|
||||
};
|
||||
const writer = dg.fwd_decl.writer();
|
||||
const writer = dg.fwdDeclWriter();
|
||||
|
||||
switch (tv.ty.zigTypeTag(mod)) {
|
||||
.Fn => if (dg.declIsGlobal(tv)) {
|
||||
@ -7517,11 +7525,25 @@ fn toAtomicRmwSuffix(order: std.builtin.AtomicRmwOp) []const u8 {
|
||||
};
|
||||
}
|
||||
|
||||
const ArrayListWriter = ErrorOnlyGenericWriter(std.ArrayList(u8).Writer.Error);
|
||||
|
||||
fn arrayListWriter(list: *std.ArrayList(u8)) ArrayListWriter {
|
||||
return .{ .context = .{
|
||||
.context = list,
|
||||
.writeFn = struct {
|
||||
fn write(context: *const anyopaque, bytes: []const u8) anyerror!usize {
|
||||
const l: *std.ArrayList(u8) = @alignCast(@constCast(@ptrCast(context)));
|
||||
return l.writer().write(bytes);
|
||||
}
|
||||
}.write,
|
||||
} };
|
||||
}
|
||||
|
||||
fn IndentWriter(comptime UnderlyingWriter: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
pub const Error = UnderlyingWriter.Error;
|
||||
pub const Writer = std.io.Writer(*Self, Error, write);
|
||||
pub const Writer = ErrorOnlyGenericWriter(Error);
|
||||
|
||||
pub const indent_delta = 1;
|
||||
|
||||
@ -7530,7 +7552,10 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {
|
||||
current_line_empty: bool = true,
|
||||
|
||||
pub fn writer(self: *Self) Writer {
|
||||
return .{ .context = self };
|
||||
return .{ .context = .{
|
||||
.context = self,
|
||||
.writeFn = writeAny,
|
||||
} };
|
||||
}
|
||||
|
||||
pub fn write(self: *Self, bytes: []const u8) Error!usize {
|
||||
@ -7545,6 +7570,11 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {
|
||||
return self.writeNoIndent(bytes);
|
||||
}
|
||||
|
||||
fn writeAny(context: *const anyopaque, bytes: []const u8) anyerror!usize {
|
||||
const self: *Self = @alignCast(@constCast(@ptrCast(context)));
|
||||
return self.write(bytes);
|
||||
}
|
||||
|
||||
pub fn insertNewline(self: *Self) Error!void {
|
||||
_ = try self.writeNoIndent("\n");
|
||||
}
|
||||
@ -7570,6 +7600,18 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
/// A wrapper around `std.io.AnyWriter` that maintains a generic error set while
|
||||
/// erasing the rest of the implementation. This is intended to avoid duplicate
|
||||
/// generic instantiations for writer types which share the same error set, while
|
||||
/// maintaining ease of error handling.
|
||||
fn ErrorOnlyGenericWriter(comptime Error: type) type {
|
||||
return std.io.GenericWriter(std.io.AnyWriter, Error, struct {
|
||||
fn write(context: std.io.AnyWriter, bytes: []const u8) Error!usize {
|
||||
return @errorCast(context.write(bytes));
|
||||
}
|
||||
}.write);
|
||||
}
|
||||
|
||||
fn toCIntBits(zig_bits: u32) ?u32 {
|
||||
for (&[_]u8{ 8, 16, 32, 64, 128 }) |c_bits| {
|
||||
if (zig_bits <= c_bits) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user