authorgravatar for ian@ianjohnson.devIan Johnson <ian@ianjohnson.dev> 2023-10-09 00:10:30-04:00
committergravatar for ian@ianjohnson.devIan Johnson <ian@ianjohnson.dev> 2024-01-29 19:02:18-05:00
loge392c1a0b33e69156c36234e6584be306caf16ef
tree752d92804098a096553dcaa1112634db93033a27
parent4dfca01de4fda9a195048011b3339686dce4e936

Add type-erased writer and GenericWriter

This is a companion to #17344 to apply the same change to the `std.io.Writer` interface.

4 files changed, 123 insertions(+), 70 deletions(-)

CMakeLists.txt+1-1
...@@ -265,7 +265,7 @@ set(ZIG_STAGE2_SOURCES...@@ -265,7 +265,7 @@ set(ZIG_STAGE2_SOURCES
265 "${CMAKE_SOURCE_DIR}/lib/std/io/limited_reader.zig"265 "${CMAKE_SOURCE_DIR}/lib/std/io/limited_reader.zig"
266 "${CMAKE_SOURCE_DIR}/lib/std/io/Reader.zig"266 "${CMAKE_SOURCE_DIR}/lib/std/io/Reader.zig"
267 "${CMAKE_SOURCE_DIR}/lib/std/io/seekable_stream.zig"267 "${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"
269 "${CMAKE_SOURCE_DIR}/lib/std/json.zig"269 "${CMAKE_SOURCE_DIR}/lib/std/json.zig"
270 "${CMAKE_SOURCE_DIR}/lib/std/json/stringify.zig"270 "${CMAKE_SOURCE_DIR}/lib/std/json/stringify.zig"
271 "${CMAKE_SOURCE_DIR}/lib/std/leb128.zig"271 "${CMAKE_SOURCE_DIR}/lib/std/leb128.zig"
lib/std/io.zig+62-2
...@@ -333,13 +333,73 @@ pub fn GenericReader(...@@ -333,13 +333,73 @@ pub fn GenericReader(
333 };333 };
334}334}
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
336/// Deprecated; consider switching to `AnyReader` or use `GenericReader`393/// Deprecated; consider switching to `AnyReader` or use `GenericReader`
337/// to use previous API.394/// to use previous API.
338pub const Reader = GenericReader;395pub const Reader = GenericReader;
396/// Deprecated; consider switching to `AnyWriter` or use `GenericWriter`
397/// to use previous API.
398pub const Writer = GenericWriter;
339399
340pub const AnyReader = @import("io/Reader.zig");400pub const AnyReader = @import("io/Reader.zig");
401pub const AnyWriter = @import("io/Writer.zig");
341402
342pub const Writer = @import("io/writer.zig").Writer;
343pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;403pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
344404
345pub const BufferedWriter = @import("io/buffered_writer.zig").BufferedWriter;405pub const BufferedWriter = @import("io/buffered_writer.zig").BufferedWriter;
...@@ -652,6 +712,7 @@ pub fn PollFiles(comptime StreamEnum: type) type {...@@ -652,6 +712,7 @@ pub fn PollFiles(comptime StreamEnum: type) type {
652712
653test {713test {
654 _ = AnyReader;714 _ = AnyReader;
715 _ = AnyWriter;
655 _ = @import("io/bit_reader.zig");716 _ = @import("io/bit_reader.zig");
656 _ = @import("io/bit_writer.zig");717 _ = @import("io/bit_writer.zig");
657 _ = @import("io/buffered_atomic_file.zig");718 _ = @import("io/buffered_atomic_file.zig");
...@@ -661,7 +722,6 @@ test {...@@ -661,7 +722,6 @@ test {
661 _ = @import("io/counting_writer.zig");722 _ = @import("io/counting_writer.zig");
662 _ = @import("io/counting_reader.zig");723 _ = @import("io/counting_reader.zig");
663 _ = @import("io/fixed_buffer_stream.zig");724 _ = @import("io/fixed_buffer_stream.zig");
664 _ = @import("io/writer.zig");
665 _ = @import("io/peek_stream.zig");725 _ = @import("io/peek_stream.zig");
666 _ = @import("io/seekable_stream.zig");726 _ = @import("io/seekable_stream.zig");
667 _ = @import("io/stream_source.zig");727 _ = @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}