Commit e392c1a0b3

Ian Johnson <ian@ianjohnson.dev>
2023-10-09 06:10:30
Add type-erased writer and GenericWriter
This is a companion to #17344 to apply the same change to the `std.io.Writer` interface.
1 parent 4dfca01
Changed files (4)
lib/std/io/Writer.zig
@@ -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));
+}
lib/std/io/writer.zig
@@ -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));
-        }
-    };
-}
lib/std/io.zig
@@ -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");
CMakeLists.txt
@@ -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"