aboutsummaryrefslogtreecommitdiff
path: root/src/anywriter.zig
diff options
context:
space:
mode:
authorMartin Ashby <martin@ashbysoft.com>2023-11-09 23:58:32 +0000
committerMartin Ashby <martin@ashbysoft.com>2023-11-09 23:58:32 +0000
commit2d65d9d3515a523d9cb8d242c3fc89671ae97d63 (patch)
treea568a510a72f4aaa484344e2e7b6a9b76a348727 /src/anywriter.zig
parent68c104c8b0580c51c9f16ec33d6a957fd4c08c0c (diff)
downloadzbt-2d65d9d3515a523d9cb8d242c3fc89671ae97d63.tar.gz
zbt-2d65d9d3515a523d9cb8d242c3fc89671ae97d63.tar.bz2
zbt-2d65d9d3515a523d9cb8d242c3fc89671ae97d63.tar.xz
zbt-2d65d9d3515a523d9cb8d242c3fc89671ae97d63.zip
Add bencoding as well as bdecoding
Diffstat (limited to 'src/anywriter.zig')
-rw-r--r--src/anywriter.zig77
1 files changed, 77 insertions, 0 deletions
diff --git a/src/anywriter.zig b/src/anywriter.zig
new file mode 100644
index 0000000..7018ac6
--- /dev/null
+++ b/src/anywriter.zig
@@ -0,0 +1,77 @@
+//! std.io.Writer drop in replacement that uses function pointer instead of
+//! compile time construct. Analogous to std.io.AnyReader.
+
+const std = @import("std");
+const AnyWriter = @This();
+pub const Error = anyerror;
+
+context: *const anyopaque,
+writeFn: *const fn (context: *const anyopaque, buffer: []const u8) anyerror!usize,
+
+// Must create a wrapper around the original writer in order to add a typeErasedWriteFn
+// and keep a reference to it alive until we're done writing to it
+// Usage might look like this:
+// var wrap = AnyWriter.wrapper(myWriter);
+// var aw = wrap.any();
+pub fn wrapper(writer: anytype) Wrapper(@TypeOf(writer)) {
+ return .{ .ww = writer };
+}
+pub fn Wrapper(comptime writerType: type) type {
+ return struct {
+ ww: writerType,
+ fn typeErasedWriteFn(context: *const anyopaque, buffer: []const u8) anyerror!usize {
+ const self: *const @This() = @alignCast(@ptrCast(context));
+ return self.ww.write(buffer);
+ }
+ pub fn any(self: *@This()) AnyWriter {
+ return .{
+ .context = @ptrCast(self),
+ .writeFn = &typeErasedWriteFn,
+ };
+ }
+ };
+}
+
+pub fn write(self: AnyWriter, bytes: []const u8) anyerror!usize {
+ return self.writeFn(self.context, bytes);
+}
+
+pub fn writeAll(self: AnyWriter, bytes: []const u8) anyerror!void {
+ var index: usize = 0;
+ while (index != bytes.len) {
+ index += try self.write(bytes[index..]);
+ }
+}
+
+pub fn print(self: AnyWriter, comptime format: []const u8, args: anytype) anyerror!void {
+ return std.fmt.format(self, format, args);
+}
+
+pub fn writeByte(self: AnyWriter, byte: u8) anyerror!void {
+ const array = [1]u8{byte};
+ return self.writeAll(&array);
+}
+
+pub fn writeByteNTimes(self: AnyWriter, 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 inline fn writeInt(self: AnyWriter, comptime T: type, value: T, endian: std.builtin.Endian) anyerror!void {
+ var bytes: [@divExact(@typeInfo(T).Int.bits, 8)]u8 = undefined;
+ std.mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian);
+ return self.writeAll(&bytes);
+}
+
+pub fn writeStruct(self: AnyWriter, value: anytype) anyerror!void {
+ // Only extern and packed structs have defined in-memory layout.
+ comptime std.debug.assert(@typeInfo(@TypeOf(value)).Struct.layout != .Auto);
+ return self.writeAll(std.mem.asBytes(&value));
+}