From 2d65d9d3515a523d9cb8d242c3fc89671ae97d63 Mon Sep 17 00:00:00 2001 From: Martin Ashby Date: Thu, 9 Nov 2023 23:58:32 +0000 Subject: Add bencoding as well as bdecoding --- src/anywriter.zig | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/anywriter.zig (limited to 'src/anywriter.zig') 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)); +} -- cgit v1.2.3-ZIG