diff options
author | Martin Ashby <martin@ashbysoft.com> | 2023-11-09 23:58:32 +0000 |
---|---|---|
committer | Martin Ashby <martin@ashbysoft.com> | 2023-11-09 23:58:32 +0000 |
commit | 2d65d9d3515a523d9cb8d242c3fc89671ae97d63 (patch) | |
tree | a568a510a72f4aaa484344e2e7b6a9b76a348727 /src/bencode.zig | |
parent | 68c104c8b0580c51c9f16ec33d6a957fd4c08c0c (diff) | |
download | zbt-2d65d9d3515a523d9cb8d242c3fc89671ae97d63.tar.gz zbt-2d65d9d3515a523d9cb8d242c3fc89671ae97d63.tar.bz2 zbt-2d65d9d3515a523d9cb8d242c3fc89671ae97d63.tar.xz zbt-2d65d9d3515a523d9cb8d242c3fc89671ae97d63.zip |
Add bencoding as well as bdecoding
Diffstat (limited to 'src/bencode.zig')
-rw-r--r-- | src/bencode.zig | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/src/bencode.zig b/src/bencode.zig index b78ee9f..44dec0d 100644 --- a/src/bencode.zig +++ b/src/bencode.zig @@ -2,6 +2,7 @@ //! See specification here https://wiki.theory.org/BitTorrentSpecification#Bencoding const std = @import("std"); +const AnyWriter = @import("anywriter.zig"); pub const Error = error.Malformatted || std.io.AnyReader.Error; @@ -12,6 +13,41 @@ pub const BValue = union(enum) { list: std.ArrayList(BValue), dict: std.StringArrayHashMap(BValue), + pub fn bencode(self: *const BValue, base_writer: anytype) !void { + var wrap = AnyWriter.wrapper(base_writer); + var writer = wrap.any(); + try self.bencodeInner(writer); + } + + // Note: uses defined types only to avoid trying to recursively evaulate this function + // at compile time, otherwise we run into https://github.com/ziglang/zig/issues/13724 + fn bencodeInner(self: *const BValue, writer: AnyWriter) !void { + switch (self.*) { + .string => |s| { + try std.fmt.format(writer, "{}:{s}", .{ s.len, s }); + }, + .list => |l| { + try writer.writeByte('l'); + for (l.items) |i| { + try i.bencodeInner(writer); + } + try writer.writeByte('e'); + }, + .dict => |d| { + try writer.writeByte('d'); + var it = d.iterator(); + while (it.next()) |entry| { + try std.fmt.format(writer, "{}:{s}", .{ entry.key_ptr.*.len, entry.key_ptr.* }); + try entry.value_ptr.*.bencodeInner(writer); + } + try writer.writeByte('e'); + }, + .int => |i| { + try std.fmt.format(writer, "i{}e", .{i}); + }, + } + } + pub fn deinit(self: *BValue, a: std.mem.Allocator) void { switch (self.*) { .string => |s| { @@ -237,3 +273,17 @@ test "nested structure" { try std.testing.expectEqualDeep(v2.*.list.items[1], BValue{ .int = 456 }); try std.testing.expectEqualStrings("nest", v2.*.list.items[2].list.items[0].string); } + +test "round trip" { + var a = std.testing.allocator; + const in = "d5:hello5:world2:hili123ei456el4:nesteee"; + var bval = try bdecodeBuf(a, in); + defer bval.deinit(a); + var bw = std.ArrayList(u8).init(a); + defer bw.deinit(); + var writer = bw.writer(); + try bval.bencode(writer); + var out = try bw.toOwnedSlice(); + defer a.free(out); + try std.testing.expectEqualStrings(in, out); +} |