From f2f1a07774a2cb1853243fcae9ddf8ed7dcd5b50 Mon Sep 17 00:00:00 2001 From: Ian Johnson Date: Mon, 9 Oct 2023 00:10:30 -0400 Subject: [PATCH] Add type-erased writer and GenericWriter This is a companion to #17344 to apply the same change to the `std.io.Writer` interface. --- CMakeLists.txt | 2 +- lib/std/io.zig | 76 +++++++++++++++++++++++++++++++++++++- lib/std/io/Writer.zig | 79 +++++++++++++++++++++++++++++++++++++++ lib/std/io/writer.zig | 86 ------------------------------------------- 4 files changed, 154 insertions(+), 89 deletions(-) create mode 100644 lib/std/io/Writer.zig delete mode 100644 lib/std/io/writer.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f3337589ce7..9c54a4a01495 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -267,7 +267,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" diff --git a/lib/std/io.zig b/lib/std/io.zig index 64ed9f757e69..82fd585cd2e6 100644 --- a/lib/std/io.zig +++ b/lib/std/io.zig @@ -349,13 +349,85 @@ 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 writeIntNative(self: Self, comptime T: type, value: T) Error!void { + return @errorCast(self.any().writeIntNative(T, value)); + } + + pub inline fn writeIntForeign(self: Self, comptime T: type, value: T) Error!void { + return @errorCast(self.any().writeIntForeign(T, value)); + } + + pub inline fn writeIntLittle(self: Self, comptime T: type, value: T) Error!void { + return @errorCast(self.any().writeIntLittle(T, value)); + } + + pub inline fn writeIntBig(self: Self, comptime T: type, value: T) Error!void { + return @errorCast(self.any().writeIntBig(T, value)); + } + + 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; @@ -668,6 +740,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"); @@ -677,7 +750,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"); diff --git a/lib/std/io/Writer.zig b/lib/std/io/Writer.zig new file mode 100644 index 000000000000..75e411345151 --- /dev/null +++ b/lib/std/io/Writer.zig @@ -0,0 +1,79 @@ +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; + } +} + +/// Write a native-endian integer. +pub fn writeIntNative(self: Self, comptime T: type, value: T) anyerror!void { + var bytes: [@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))]u8 = undefined; + mem.writeIntNative(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value); + return self.writeAll(&bytes); +} + +/// Write a foreign-endian integer. +pub fn writeIntForeign(self: Self, comptime T: type, value: T) anyerror!void { + var bytes: [@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))]u8 = undefined; + mem.writeIntForeign(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value); + return self.writeAll(&bytes); +} + +pub fn writeIntLittle(self: Self, comptime T: type, value: T) anyerror!void { + var bytes: [@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))]u8 = undefined; + mem.writeIntLittle(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value); + return self.writeAll(&bytes); +} + +pub fn writeIntBig(self: Self, comptime T: type, value: T) anyerror!void { + var bytes: [@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))]u8 = undefined; + mem.writeIntBig(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value); + return self.writeAll(&bytes); +} + +pub fn writeInt(self: Self, comptime T: type, value: T, endian: std.builtin.Endian) anyerror!void { + var bytes: [@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 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)); +} diff --git a/lib/std/io/writer.zig b/lib/std/io/writer.zig deleted file mode 100644 index 41dcf9b6e73c..000000000000 --- a/lib/std/io/writer.zig +++ /dev/null @@ -1,86 +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; - } - } - - /// Write a native-endian integer. - pub fn writeIntNative(self: Self, comptime T: type, value: T) Error!void { - var bytes: [@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))]u8 = undefined; - mem.writeIntNative(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value); - return self.writeAll(&bytes); - } - - /// Write a foreign-endian integer. - pub fn writeIntForeign(self: Self, comptime T: type, value: T) Error!void { - var bytes: [@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))]u8 = undefined; - mem.writeIntForeign(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value); - return self.writeAll(&bytes); - } - - pub fn writeIntLittle(self: Self, comptime T: type, value: T) Error!void { - var bytes: [@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))]u8 = undefined; - mem.writeIntLittle(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value); - return self.writeAll(&bytes); - } - - pub fn writeIntBig(self: Self, comptime T: type, value: T) Error!void { - var bytes: [@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))]u8 = undefined; - mem.writeIntBig(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value); - return self.writeAll(&bytes); - } - - pub fn writeInt(self: Self, comptime T: type, value: T, endian: std.builtin.Endian) Error!void { - var bytes: [@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 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)); - } - }; -}