authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-07 23:52:53-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-02-07 23:52:53-08:00
log3122fd0ba0eb4cdb17616658b462a255a37f1ad7
tree2564a986d66fab02726aa405d23df0c645b27db5
parent9ca6cc1e2f6af1b5197f92e4b56865ab822f4040
parent2b2c9c5db893c7bc0352d1738c1658a5fcd32d62
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #17634 from ianprime0509/type-erased-writer

Add type-erased writer and GenericWriter

6 files changed, 181 insertions(+), 83 deletions(-)

CMakeLists.txt+1-1
......@@ -265,7 +265,7 @@ set(ZIG_STAGE2_SOURCES
265265 "${CMAKE_SOURCE_DIR}/lib/std/io/limited_reader.zig"
266266 "${CMAKE_SOURCE_DIR}/lib/std/io/Reader.zig"
267267 "${CMAKE_SOURCE_DIR}/lib/std/io/seekable_stream.zig"
268 "${CMAKE_SOURCE_DIR}/lib/std/io/writer.zig"
268 "${CMAKE_SOURCE_DIR}/lib/std/io/Writer.zig"
269269 "${CMAKE_SOURCE_DIR}/lib/std/json.zig"
270270 "${CMAKE_SOURCE_DIR}/lib/std/json/stringify.zig"
271271 "${CMAKE_SOURCE_DIR}/lib/std/leb128.zig"
lib/std/fmt.zig+5-2
......@@ -1986,7 +1986,10 @@ pub const BufPrintError = error{
19861986/// Returns a slice of the bytes printed to.
19871987pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![]u8 {
19881988 var fbs = std.io.fixedBufferStream(buf);
1989 try format(fbs.writer(), fmt, args);
1989 format(fbs.writer().any(), fmt, args) catch |err| switch (err) {
1990 error.NoSpaceLeft => return error.NoSpaceLeft,
1991 else => unreachable,
1992 };
19901993 return fbs.getWritten();
19911994}
19921995
......@@ -1998,7 +2001,7 @@ pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErr
19982001/// Count the characters needed for format. Useful for preallocating memory
19992002pub fn count(comptime fmt: []const u8, args: anytype) u64 {
20002003 var counting_writer = std.io.countingWriter(std.io.null_writer);
2001 format(counting_writer.writer(), fmt, args) catch |err| switch (err) {};
2004 format(counting_writer.writer().any(), fmt, args) catch unreachable;
20022005 return counting_writer.bytes_written;
20032006}
20042007
lib/std/io.zig+62-2
......@@ -333,13 +333,73 @@ pub fn GenericReader(
333333 };
334334}
335335
336pub fn GenericWriter(
337 comptime Context: type,
338 comptime WriteError: type,
339 comptime writeFn: fn (context: Context, bytes: []const u8) WriteError!usize,
340) type {
341 return struct {
342 context: Context,
343
344 const Self = @This();
345 pub const Error = WriteError;
346
347 pub inline fn write(self: Self, bytes: []const u8) Error!usize {
348 return writeFn(self.context, bytes);
349 }
350
351 pub inline fn writeAll(self: Self, bytes: []const u8) Error!void {
352 return @errorCast(self.any().writeAll(bytes));
353 }
354
355 pub inline fn print(self: Self, comptime format: []const u8, args: anytype) Error!void {
356 return @errorCast(self.any().print(format, args));
357 }
358
359 pub inline fn writeByte(self: Self, byte: u8) Error!void {
360 return @errorCast(self.any().writeByte(byte));
361 }
362
363 pub inline fn writeByteNTimes(self: Self, byte: u8, n: usize) Error!void {
364 return @errorCast(self.any().writeByteNTimes(byte, n));
365 }
366
367 pub inline fn writeBytesNTimes(self: Self, bytes: []const u8, n: usize) Error!void {
368 return @errorCast(self.any().writeBytesNTimes(bytes, n));
369 }
370
371 pub inline fn writeInt(self: Self, comptime T: type, value: T, endian: std.builtin.Endian) Error!void {
372 return @errorCast(self.any().writeInt(T, value, endian));
373 }
374
375 pub inline fn writeStruct(self: Self, value: anytype) Error!void {
376 return @errorCast(self.any().writeStruct(value));
377 }
378
379 pub inline fn any(self: *const Self) AnyWriter {
380 return .{
381 .context = @ptrCast(&self.context),
382 .writeFn = typeErasedWriteFn,
383 };
384 }
385
386 fn typeErasedWriteFn(context: *const anyopaque, bytes: []const u8) anyerror!usize {
387 const ptr: *const Context = @alignCast(@ptrCast(context));
388 return writeFn(ptr.*, bytes);
389 }
390 };
391}
392
336393/// Deprecated; consider switching to `AnyReader` or use `GenericReader`
337394/// to use previous API.
338395pub const Reader = GenericReader;
396/// Deprecated; consider switching to `AnyWriter` or use `GenericWriter`
397/// to use previous API.
398pub const Writer = GenericWriter;
339399
340400pub const AnyReader = @import("io/Reader.zig");
401pub const AnyWriter = @import("io/Writer.zig");
341402
342pub const Writer = @import("io/writer.zig").Writer;
343403pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
344404
345405pub const BufferedWriter = @import("io/buffered_writer.zig").BufferedWriter;
......@@ -652,6 +712,7 @@ pub fn PollFiles(comptime StreamEnum: type) type {
652712
653713test {
654714 _ = AnyReader;
715 _ = AnyWriter;
655716 _ = @import("io/bit_reader.zig");
656717 _ = @import("io/bit_writer.zig");
657718 _ = @import("io/buffered_atomic_file.zig");
......@@ -661,7 +722,6 @@ test {
661722 _ = @import("io/counting_writer.zig");
662723 _ = @import("io/counting_reader.zig");
663724 _ = @import("io/fixed_buffer_stream.zig");
664 _ = @import("io/writer.zig");
665725 _ = @import("io/peek_stream.zig");
666726 _ = @import("io/seekable_stream.zig");
667727 _ = @import("io/stream_source.zig");
lib/std/io/Writer.zig created+60
......@@ -0,0 +1,60 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const mem = std.mem;
4
5context: *const anyopaque,
6writeFn: *const fn (context: *const anyopaque, bytes: []const u8) anyerror!usize,
7
8const Self = @This();
9pub const Error = anyerror;
10
11pub fn write(self: Self, bytes: []const u8) anyerror!usize {
12 return self.writeFn(self.context, bytes);
13}
14
15pub fn writeAll(self: Self, bytes: []const u8) anyerror!void {
16 var index: usize = 0;
17 while (index != bytes.len) {
18 index += try self.write(bytes[index..]);
19 }
20}
21
22pub fn print(self: Self, comptime format: []const u8, args: anytype) anyerror!void {
23 return std.fmt.format(self, format, args);
24}
25
26pub fn writeByte(self: Self, byte: u8) anyerror!void {
27 const array = [1]u8{byte};
28 return self.writeAll(&array);
29}
30
31pub fn writeByteNTimes(self: Self, byte: u8, n: usize) anyerror!void {
32 var bytes: [256]u8 = undefined;
33 @memset(bytes[0..], byte);
34
35 var remaining: usize = n;
36 while (remaining > 0) {
37 const to_write = @min(remaining, bytes.len);
38 try self.writeAll(bytes[0..to_write]);
39 remaining -= to_write;
40 }
41}
42
43pub fn writeBytesNTimes(self: Self, bytes: []const u8, n: usize) anyerror!void {
44 var i: usize = 0;
45 while (i < n) : (i += 1) {
46 try self.writeAll(bytes);
47 }
48}
49
50pub inline fn writeInt(self: Self, comptime T: type, value: T, endian: std.builtin.Endian) anyerror!void {
51 var bytes: [@divExact(@typeInfo(T).Int.bits, 8)]u8 = undefined;
52 mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian);
53 return self.writeAll(&bytes);
54}
55
56pub fn writeStruct(self: Self, value: anytype) anyerror!void {
57 // Only extern and packed structs have defined in-memory layout.
58 comptime assert(@typeInfo(@TypeOf(value)).Struct.layout != .Auto);
59 return self.writeAll(mem.asBytes(&value));
60}
lib/std/io/writer.zig deleted-67
......@@ -1,67 +0,0 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const mem = std.mem;
4
5pub fn Writer(
6 comptime Context: type,
7 comptime WriteError: type,
8 comptime writeFn: fn (context: Context, bytes: []const u8) WriteError!usize,
9) type {
10 return struct {
11 context: Context,
12
13 const Self = @This();
14 pub const Error = WriteError;
15
16 pub fn write(self: Self, bytes: []const u8) Error!usize {
17 return writeFn(self.context, bytes);
18 }
19
20 pub fn writeAll(self: Self, bytes: []const u8) Error!void {
21 var index: usize = 0;
22 while (index != bytes.len) {
23 index += try self.write(bytes[index..]);
24 }
25 }
26
27 pub fn print(self: Self, comptime format: []const u8, args: anytype) Error!void {
28 return std.fmt.format(self, format, args);
29 }
30
31 pub fn writeByte(self: Self, byte: u8) Error!void {
32 const array = [1]u8{byte};
33 return self.writeAll(&array);
34 }
35
36 pub fn writeByteNTimes(self: Self, byte: u8, n: usize) Error!void {
37 var bytes: [256]u8 = undefined;
38 @memset(bytes[0..], byte);
39
40 var remaining: usize = n;
41 while (remaining > 0) {
42 const to_write = @min(remaining, bytes.len);
43 try self.writeAll(bytes[0..to_write]);
44 remaining -= to_write;
45 }
46 }
47
48 pub fn writeBytesNTimes(self: Self, bytes: []const u8, n: usize) Error!void {
49 var i: usize = 0;
50 while (i < n) : (i += 1) {
51 try self.writeAll(bytes);
52 }
53 }
54
55 pub inline fn writeInt(self: Self, comptime T: type, value: T, endian: std.builtin.Endian) Error!void {
56 var bytes: [@divExact(@typeInfo(T).Int.bits, 8)]u8 = undefined;
57 mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian);
58 return self.writeAll(&bytes);
59 }
60
61 pub fn writeStruct(self: Self, value: anytype) Error!void {
62 // Only extern and packed structs have defined in-memory layout.
63 comptime assert(@typeInfo(@TypeOf(value)).Struct.layout != .Auto);
64 return self.writeAll(mem.asBytes(&value));
65 }
66 };
67}
src/codegen/c.zig+53-11
......@@ -306,7 +306,7 @@ pub const Function = struct {
306306 const ty = f.typeOf(ref);
307307
308308 const result: CValue = if (lowersToArray(ty, mod)) result: {
309 const writer = f.object.code_header.writer();
309 const writer = f.object.codeHeaderWriter();
310310 const alignment: Alignment = .none;
311311 const decl_c_value = try f.allocLocalValue(ty, alignment);
312312 const gpa = f.object.dg.gpa;
......@@ -534,6 +534,10 @@ pub const Object = struct {
534534 fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer {
535535 return o.indent_writer.writer();
536536 }
537
538 fn codeHeaderWriter(o: *Object) ArrayListWriter {
539 return arrayListWriter(&o.code_header);
540 }
537541};
538542
539543/// This data is available both when outputting .c code and when outputting an .h file.
......@@ -557,6 +561,10 @@ pub const DeclGen = struct {
557561 flush,
558562 };
559563
564 fn fwdDeclWriter(dg: *DeclGen) ArrayListWriter {
565 return arrayListWriter(&dg.fwd_decl);
566 }
567
560568 fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
561569 @setCold(true);
562570 const mod = dg.module;
......@@ -1982,7 +1990,7 @@ pub const DeclGen = struct {
19821990 fwd_kind: enum { tentative, final },
19831991 ) !void {
19841992 const decl = dg.module.declPtr(decl_index);
1985 const fwd = dg.fwd_decl.writer();
1993 const fwd = dg.fwdDeclWriter();
19861994 const is_global = variable.is_extern or dg.declIsGlobal(.{ .ty = decl.ty, .val = decl.val });
19871995 try fwd.writeAll(if (is_global) "zig_extern " else "static ");
19881996 const maybe_exports = dg.module.decl_exports.get(decl_index);
......@@ -2668,7 +2676,7 @@ fn genExports(o: *Object) !void {
26682676 };
26692677 const decl = mod.declPtr(decl_index);
26702678 const tv: TypedValue = .{ .ty = decl.ty, .val = Value.fromInterned((try decl.internValue(mod))) };
2671 const fwd = o.dg.fwd_decl.writer();
2679 const fwd = o.dg.fwdDeclWriter();
26722680
26732681 const exports = mod.decl_exports.get(decl_index) orelse return;
26742682 if (exports.items.len < 2) return;
......@@ -2782,7 +2790,7 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
27822790 const fn_cty = try o.dg.typeToCType(fn_decl.ty, .complete);
27832791 const fn_info = fn_cty.cast(CType.Payload.Function).?.data;
27842792
2785 const fwd_decl_writer = o.dg.fwd_decl.writer();
2793 const fwd_decl_writer = o.dg.fwdDeclWriter();
27862794 try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)});
27872795 try o.dg.renderFunctionSignature(
27882796 fwd_decl_writer,
......@@ -2824,7 +2832,7 @@ pub fn genFunc(f: *Function) !void {
28242832 defer o.code_header.deinit();
28252833
28262834 const is_global = o.dg.declIsGlobal(tv);
2827 const fwd_decl_writer = o.dg.fwd_decl.writer();
2835 const fwd_decl_writer = o.dg.fwdDeclWriter();
28282836 try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");
28292837
28302838 if (mod.decl_exports.get(decl_index)) |exports|
......@@ -2879,7 +2887,7 @@ pub fn genFunc(f: *Function) !void {
28792887 };
28802888 free_locals.sort(SortContext{ .keys = free_locals.keys() });
28812889
2882 const w = o.code_header.writer();
2890 const w = o.codeHeaderWriter();
28832891 for (free_locals.values()) |list| {
28842892 for (list.keys()) |local_index| {
28852893 const local = f.locals.items[local_index];
......@@ -2907,7 +2915,7 @@ pub fn genDecl(o: *Object) !void {
29072915
29082916 if (!tv.ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) return;
29092917 if (tv.val.getExternFunc(mod)) |_| {
2910 const fwd_decl_writer = o.dg.fwd_decl.writer();
2918 const fwd_decl_writer = o.dg.fwdDeclWriter();
29112919 try fwd_decl_writer.writeAll("zig_extern ");
29122920 try o.dg.renderFunctionSignature(fwd_decl_writer, decl_index, .forward, .{ .export_index = 0 });
29132921 try fwd_decl_writer.writeAll(";\n");
......@@ -2948,7 +2956,7 @@ pub fn genDeclValue(
29482956 link_section: InternPool.OptionalNullTerminatedString,
29492957) !void {
29502958 const mod = o.dg.module;
2951 const fwd_decl_writer = o.dg.fwd_decl.writer();
2959 const fwd_decl_writer = o.dg.fwdDeclWriter();
29522960
29532961 try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");
29542962 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 {
29923000 .ty = decl.ty,
29933001 .val = decl.val,
29943002 };
2995 const writer = dg.fwd_decl.writer();
3003 const writer = dg.fwdDeclWriter();
29963004
29973005 switch (tv.ty.zigTypeTag(mod)) {
29983006 .Fn => if (dg.declIsGlobal(tv)) {
......@@ -7517,11 +7525,25 @@ fn toAtomicRmwSuffix(order: std.builtin.AtomicRmwOp) []const u8 {
75177525 };
75187526}
75197527
7528const ArrayListWriter = ErrorOnlyGenericWriter(std.ArrayList(u8).Writer.Error);
7529
7530fn arrayListWriter(list: *std.ArrayList(u8)) ArrayListWriter {
7531 return .{ .context = .{
7532 .context = list,
7533 .writeFn = struct {
7534 fn write(context: *const anyopaque, bytes: []const u8) anyerror!usize {
7535 const l: *std.ArrayList(u8) = @alignCast(@constCast(@ptrCast(context)));
7536 return l.writer().write(bytes);
7537 }
7538 }.write,
7539 } };
7540}
7541
75207542fn IndentWriter(comptime UnderlyingWriter: type) type {
75217543 return struct {
75227544 const Self = @This();
75237545 pub const Error = UnderlyingWriter.Error;
7524 pub const Writer = std.io.Writer(*Self, Error, write);
7546 pub const Writer = ErrorOnlyGenericWriter(Error);
75257547
75267548 pub const indent_delta = 1;
75277549
......@@ -7530,7 +7552,10 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {
75307552 current_line_empty: bool = true,
75317553
75327554 pub fn writer(self: *Self) Writer {
7533 return .{ .context = self };
7555 return .{ .context = .{
7556 .context = self,
7557 .writeFn = writeAny,
7558 } };
75347559 }
75357560
75367561 pub fn write(self: *Self, bytes: []const u8) Error!usize {
......@@ -7545,6 +7570,11 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {
75457570 return self.writeNoIndent(bytes);
75467571 }
75477572
7573 fn writeAny(context: *const anyopaque, bytes: []const u8) anyerror!usize {
7574 const self: *Self = @alignCast(@constCast(@ptrCast(context)));
7575 return self.write(bytes);
7576 }
7577
75487578 pub fn insertNewline(self: *Self) Error!void {
75497579 _ = try self.writeNoIndent("\n");
75507580 }
......@@ -7570,6 +7600,18 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {
75707600 };
75717601}
75727602
7603/// A wrapper around `std.io.AnyWriter` that maintains a generic error set while
7604/// erasing the rest of the implementation. This is intended to avoid duplicate
7605/// generic instantiations for writer types which share the same error set, while
7606/// maintaining ease of error handling.
7607fn ErrorOnlyGenericWriter(comptime Error: type) type {
7608 return std.io.GenericWriter(std.io.AnyWriter, Error, struct {
7609 fn write(context: std.io.AnyWriter, bytes: []const u8) Error!usize {
7610 return @errorCast(context.write(bytes));
7611 }
7612 }.write);
7613}
7614
75737615fn toCIntBits(zig_bits: u32) ?u32 {
75747616 for (&[_]u8{ 8, 16, 32, 64, 128 }) |c_bits| {
75757617 if (zig_bits <= c_bits) {